Make a mock messenger that can easily mock channels (flutter/engine#56867)

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.
This commit is contained in:
Robert Ancell 2024-12-03 13:41:33 +13:00 committed by GitHub
parent 3cb4a84de6
commit 14ae687eac
19 changed files with 2134 additions and 1660 deletions

View File

@ -249,11 +249,10 @@ executable("flutter_linux_unittests") {
"fl_view_test.cc", "fl_view_test.cc",
"fl_window_state_monitor_test.cc", "fl_window_state_monitor_test.cc",
"key_mapping_test.cc", "key_mapping_test.cc",
"testing/fl_mock_binary_messenger.cc",
"testing/fl_test.cc", "testing/fl_test.cc",
"testing/fl_test_gtk_logs.cc", "testing/fl_test_gtk_logs.cc",
"testing/fl_test_gtk_logs.h", "testing/fl_test_gtk_logs.h",
"testing/mock_binary_messenger.cc",
"testing/mock_binary_messenger_response_handle.cc",
"testing/mock_engine.cc", "testing/mock_engine.cc",
"testing/mock_epoxy.cc", "testing/mock_epoxy.cc",
"testing/mock_im_context.cc", "testing/mock_im_context.cc",

View File

@ -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_method_channel.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.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/fl_test.h"
#include "flutter/shell/platform/linux/testing/mock_binary_messenger_response_handle.h"
#include "flutter/shell/platform/linux/testing/mock_renderer.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, G_DECLARE_FINAL_TYPE(FlFakeBinaryMessenger,
fl_fake_binary_messenger, fl_fake_binary_messenger,
FL, FL,
@ -55,7 +80,7 @@ static gboolean send_message_cb(gpointer user_data) {
g_autoptr(GBytes) message = g_bytes_new(text, strlen(text)); g_autoptr(GBytes) message = g_bytes_new(text, strlen(text));
self->message_handler(FL_BINARY_MESSENGER(self), "CHANNEL", message, self->message_handler(FL_BINARY_MESSENGER(self), "CHANNEL", message,
FL_BINARY_MESSENGER_RESPONSE_HANDLE( FL_BINARY_MESSENGER_RESPONSE_HANDLE(
fl_mock_binary_messenger_response_handle_new()), fl_fake_binary_messenger_response_handle_new()),
self->message_handler_user_data); self->message_handler_user_data);
return FALSE; return FALSE;
@ -83,7 +108,7 @@ static gboolean send_response(FlBinaryMessenger* messenger,
GError** error) { GError** error) {
FlFakeBinaryMessenger* self = FL_FAKE_BINARY_MESSENGER(messenger); 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_autofree gchar* text =
g_strndup(static_cast<const gchar*>(g_bytes_get_data(response, nullptr)), g_strndup(static_cast<const gchar*>(g_bytes_get_data(response, nullptr)),

View File

@ -94,8 +94,6 @@ struct _FlKeyChannelResponder {
GObject parent_instance; GObject parent_instance;
FlBasicMessageChannel* channel; FlBasicMessageChannel* channel;
FlKeyChannelResponderMock* mock;
}; };
G_DEFINE_TYPE(FlKeyChannelResponder, fl_key_channel_responder, G_TYPE_OBJECT) 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); FlBasicMessageChannel* messageChannel = FL_BASIC_MESSAGE_CHANNEL(object);
FlValue* message = FlValue* message =
fl_basic_message_channel_send_finish(messageChannel, result, &error); 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; bool handled = false;
if (error != nullptr) { if (error != nullptr) {
g_warning("Unable to retrieve framework response: %s", error->message); 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 // Creates a new FlKeyChannelResponder instance, with a messenger used to send
// messages to the framework, and an FlTextInputHandler that is used to handle // 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 // key events that the framework doesn't handle.
// also takes an optional callback to call when a response is received, and an
// optional channel name to use when sending messages.
FlKeyChannelResponder* fl_key_channel_responder_new( FlKeyChannelResponder* fl_key_channel_responder_new(
FlBinaryMessenger* messenger, FlBinaryMessenger* messenger) {
FlKeyChannelResponderMock* mock) {
g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr); g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
FlKeyChannelResponder* self = FL_KEY_CHANNEL_RESPONDER( FlKeyChannelResponder* self = FL_KEY_CHANNEL_RESPONDER(
g_object_new(fl_key_channel_responder_get_type(), nullptr)); g_object_new(fl_key_channel_responder_get_type(), nullptr));
self->mock = mock;
g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new(); g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
const char* channel_name = self->channel = fl_basic_message_channel_new(messenger, kChannelName,
mock == nullptr ? kChannelName : mock->channel_name;
self->channel = fl_basic_message_channel_new(messenger, channel_name,
FL_MESSAGE_CODEC(codec)); FL_MESSAGE_CODEC(codec));
return self; return self;

View File

@ -11,27 +11,6 @@
typedef FlValue* (*FlValueConverter)(FlValue*); 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_BEGIN_DECLS
G_DECLARE_FINAL_TYPE(FlKeyChannelResponder, G_DECLARE_FINAL_TYPE(FlKeyChannelResponder,
@ -64,15 +43,13 @@ typedef void (*FlKeyChannelResponderAsyncCallback)(bool handled,
/** /**
* fl_key_channel_responder_new: * fl_key_channel_responder_new:
* @messenger: the messenger that the message channel should be built on. * @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. * Creates a new #FlKeyChannelResponder.
* *
* Returns: a new #FlKeyChannelResponder. * Returns: a new #FlKeyChannelResponder.
*/ */
FlKeyChannelResponder* fl_key_channel_responder_new( FlKeyChannelResponder* fl_key_channel_responder_new(
FlBinaryMessenger* messenger, FlBinaryMessenger* messenger);
FlKeyChannelResponderMock* mock = nullptr);
/** /**
* fl_key_channel_responder_handle_event: * fl_key_channel_responder_handle_event:

View File

@ -7,64 +7,81 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "flutter/shell/platform/linux/fl_binary_messenger_private.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_mock_binary_messenger.h"
#include "flutter/shell/platform/linux/testing/fl_test.h"
static const char* expected_value = nullptr; typedef struct {
static gboolean expected_handled = FALSE; const gchar* expected_message;
gboolean handled;
} KeyEventData;
static FlValue* echo_response_cb(FlValue* echoed_value) { static FlValue* key_event_cb(FlMockBinaryMessenger* messenger,
gchar* text = fl_value_to_string(echoed_value); FlValue* message,
EXPECT_STREQ(text, expected_value); gpointer user_data) {
g_free(text); KeyEventData* data = static_cast<KeyEventData*>(user_data);
FlValue* value = fl_value_new_map(); g_autofree gchar* message_string = fl_value_to_string(message);
fl_value_set_string_take(value, "handled", EXPECT_STREQ(message_string, data->expected_message);
fl_value_new_bool(expected_handled));
return value; 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) { static void set_key_event_channel(FlMockBinaryMessenger* messenger,
EXPECT_EQ(handled, expected_handled); const gchar* expected_message,
g_main_loop_quit(static_cast<GMainLoop*>(user_data)); 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 sending a letter "A";
TEST(FlKeyChannelResponderTest, SendKeyEvent) { TEST(FlKeyChannelResponderTest, SendKeyEvent) {
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
g_autoptr(FlEngine) engine = make_mock_engine(); g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine);
FlKeyChannelResponderMock mock{
.value_converter = echo_response_cb,
.channel_name = "test/echo",
};
g_autoptr(FlKeyChannelResponder) responder = 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( g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
12345, TRUE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0); 12345, TRUE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0);
fl_key_channel_responder_handle_event(responder, event1, 0, fl_key_channel_responder_handle_event(
responder_callback, loop); responder, event1, 0,
expected_value = [](bool handled, gpointer user_data) {
"{type: keydown, keymap: linux, scanCode: 4, toolkit: gtk, keyCode: 65, " EXPECT_FALSE(handled);
"modifiers: 0, unicodeScalarValues: 65}"; g_main_loop_quit(static_cast<GMainLoop*>(user_data));
expected_handled = FALSE; },
loop);
// Blocks here until echo_response_cb is called.
g_main_loop_run(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( g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
23456, FALSE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0); 23456, FALSE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0);
fl_key_channel_responder_handle_event(responder, event2, 0, fl_key_channel_responder_handle_event(
responder_callback, loop); responder, event2, 0,
expected_value = [](bool handled, gpointer user_data) {
"{type: keyup, keymap: linux, scanCode: 4, toolkit: gtk, keyCode: 65, " EXPECT_FALSE(handled);
"modifiers: 0, unicodeScalarValues: 65}"; g_main_loop_quit(static_cast<GMainLoop*>(user_data));
expected_handled = FALSE; },
loop);
// Blocks here until echo_response_cb is called.
g_main_loop_run(loop); g_main_loop_run(loop);
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
} }
void test_lock_event(guint key_code, void test_lock_event(guint key_code,
@ -72,34 +89,35 @@ void test_lock_event(guint key_code,
const char* up_expected) { const char* up_expected) {
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
g_autoptr(FlEngine) engine = make_mock_engine(); g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine);
FlKeyChannelResponderMock mock{
.value_converter = echo_response_cb,
.channel_name = "test/echo",
};
g_autoptr(FlKeyChannelResponder) responder = 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( g_autoptr(FlKeyEvent) event1 = fl_key_event_new(
12345, TRUE, 0x04, key_code, static_cast<GdkModifierType>(0), 0); 12345, TRUE, 0x04, key_code, static_cast<GdkModifierType>(0), 0);
fl_key_channel_responder_handle_event(responder, event1, 0, fl_key_channel_responder_handle_event(
responder_callback, loop); responder, event1, 0,
expected_value = down_expected; [](bool handled, gpointer user_data) {
expected_handled = FALSE; EXPECT_FALSE(handled);
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
// Blocks here until echo_response_cb is called. },
loop);
g_main_loop_run(loop); g_main_loop_run(loop);
expected_value = up_expected; set_key_event_channel(messenger, up_expected, FALSE);
expected_handled = FALSE;
g_autoptr(FlKeyEvent) event2 = fl_key_event_new( g_autoptr(FlKeyEvent) event2 = fl_key_event_new(
12346, FALSE, 0x04, key_code, static_cast<GdkModifierType>(0), 0); 12346, FALSE, 0x04, key_code, static_cast<GdkModifierType>(0), 0);
fl_key_channel_responder_handle_event(responder, event2, 0, fl_key_channel_responder_handle_event(
responder_callback, loop); responder, event2, 0,
[](bool handled, gpointer user_data) {
// Blocks here until echo_response_cb is called. EXPECT_FALSE(handled);
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
},
loop);
g_main_loop_run(loop); g_main_loop_run(loop);
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
} }
// Test sending a "NumLock" keypress. // Test sending a "NumLock" keypress.
@ -132,50 +150,52 @@ TEST(FlKeyChannelResponderTest, SendShiftLockKeyEvent) {
TEST(FlKeyChannelResponderTest, TestKeyEventHandledByFramework) { TEST(FlKeyChannelResponderTest, TestKeyEventHandledByFramework) {
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
g_autoptr(FlEngine) engine = make_mock_engine(); g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine);
FlKeyChannelResponderMock mock{
.value_converter = echo_response_cb,
.channel_name = "test/echo",
};
g_autoptr(FlKeyChannelResponder) responder = 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( g_autoptr(FlKeyEvent) event = fl_key_event_new(
12345, TRUE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0); 12345, TRUE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0);
fl_key_channel_responder_handle_event(responder, event, 0, responder_callback, fl_key_channel_responder_handle_event(
loop); responder, event, 0,
expected_handled = TRUE; [](bool handled, gpointer user_data) {
expected_value = EXPECT_TRUE(handled);
"{type: keydown, keymap: linux, scanCode: 4, toolkit: gtk, " g_main_loop_quit(static_cast<GMainLoop*>(user_data));
"keyCode: 65, modifiers: 0, unicodeScalarValues: 65}"; },
loop);
// Blocks here until echo_response_cb is called.
g_main_loop_run(loop); g_main_loop_run(loop);
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
} }
TEST(FlKeyChannelResponderTest, UseSpecifiedLogicalKey) { TEST(FlKeyChannelResponderTest, UseSpecifiedLogicalKey) {
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
g_autoptr(FlEngine) engine = make_mock_engine(); g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlBinaryMessenger) messenger = fl_binary_messenger_new(engine);
FlKeyChannelResponderMock mock{
.value_converter = echo_response_cb,
.channel_name = "test/echo",
};
g_autoptr(FlKeyChannelResponder) responder = 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( set_key_event_channel(
12345, TRUE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(0), 0); messenger,
fl_key_channel_responder_handle_event(responder, event, 888,
responder_callback, loop);
expected_handled = TRUE;
expected_value =
"{type: keydown, keymap: linux, scanCode: 4, toolkit: gtk, " "{type: keydown, keymap: linux, scanCode: 4, toolkit: gtk, "
"keyCode: 65, modifiers: 0, unicodeScalarValues: 65, " "keyCode: 65, modifiers: 0, unicodeScalarValues: 65, "
"specifiedLogicalKey: 888}"; "specifiedLogicalKey: 888}",
TRUE);
// Blocks here until echo_response_cb is called. g_autoptr(FlKeyEvent) event = fl_key_event_new(
12345, TRUE, 0x04, GDK_KEY_A, static_cast<GdkModifierType>(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<GMainLoop*>(user_data));
},
loop);
g_main_loop_run(loop); g_main_loop_run(loop);
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
} }

View File

@ -4,9 +4,10 @@
#include "flutter/shell/platform/linux/fl_keyboard_handler.h" #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/fl_method_codec_private.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.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 "gmock/gmock.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
@ -26,19 +27,6 @@ G_DECLARE_FINAL_TYPE(FlMockKeyboardHandlerDelegate,
G_END_DECLS 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 { struct _FlMockKeyboardHandlerDelegate {
GObject parent_instance; GObject parent_instance;
}; };
@ -74,8 +62,7 @@ static FlMockKeyboardHandlerDelegate* fl_mock_keyboard_handler_delegate_new() {
} }
TEST(FlKeyboardHandlerTest, KeyboardChannelGetPressedState) { TEST(FlKeyboardHandlerTest, KeyboardChannelGetPressedState) {
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine = g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr)); FL_BINARY_MESSENGER(messenger), nullptr));
@ -95,21 +82,28 @@ TEST(FlKeyboardHandlerTest, KeyboardChannelGetPressedState) {
}, },
nullptr); nullptr);
g_autoptr(FlKeyboardHandler) handler = g_autoptr(FlKeyboardHandler) handler =
fl_keyboard_handler_new(messenger, manager); fl_keyboard_handler_new(FL_BINARY_MESSENGER(messenger), manager);
EXPECT_NE(handler, nullptr); EXPECT_NE(handler, nullptr);
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); gboolean called = FALSE;
g_autoptr(GBytes) message = fl_method_codec_encode_method_call( fl_mock_binary_messenger_invoke_standard_method(
FL_METHOD_CODEC(codec), kGetKeyboardStateMethod, nullptr, nullptr); messenger, kKeyboardChannelName, kGetKeyboardStateMethod, nullptr,
[](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
gpointer user_data) {
gboolean* called = static_cast<gboolean*>(user_data);
*called = TRUE;
g_autoptr(FlValue) response = fl_value_new_map(); EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
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<FlBinaryMessenger*>(messenger), ::testing::_,
MethodSuccessResponse(response), ::testing::_))
.WillOnce(::testing::Return(true));
messenger.ReceiveMessage(kKeyboardChannelName, message); 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);
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
} }

View File

@ -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_METHOD_CHANNEL(self), FALSE);
g_return_val_if_fail(FL_IS_BINARY_MESSENGER_RESPONSE_HANDLE(response_handle), g_return_val_if_fail(FL_IS_BINARY_MESSENGER_RESPONSE_HANDLE(response_handle),
FALSE); FALSE);
g_return_val_if_fail(FL_IS_METHOD_SUCCESS_RESPONSE(response) || g_return_val_if_fail(FL_IS_METHOD_RESPONSE(response), FALSE);
FL_IS_METHOD_ERROR_RESPONSE(response) ||
FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response),
FALSE);
g_autoptr(GBytes) message = nullptr; g_autoptr(GBytes) message =
if (FL_IS_METHOD_SUCCESS_RESPONSE(response)) { fl_method_codec_encode_response(self->codec, response, error);
FlMethodSuccessResponse* r = FL_METHOD_SUCCESS_RESPONSE(response); if (message == nullptr) {
message = fl_method_codec_encode_success_envelope( return FALSE;
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();
} }
return fl_binary_messenger_send_response(self->messenger, response_handle, return fl_binary_messenger_send_response(self->messenger, response_handle,
message, error); message, error);
} }

View File

@ -59,6 +59,32 @@ GBytes* fl_method_codec_encode_error_envelope(FlMethodCodec* self,
self, code, message, details, error); 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, FlMethodResponse* fl_method_codec_decode_response(FlMethodCodec* self,
GBytes* message, GBytes* message,
GError** error) { GError** error) {

View File

@ -85,6 +85,21 @@ GBytes* fl_method_codec_encode_error_envelope(FlMethodCodec* codec,
FlValue* details, FlValue* details,
GError** error); 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: * fl_method_codec_decode_response:
* @codec: an #FlMethodCodec. * @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 * @error: (allow-none): #GError location to store the error occurring, or
* %NULL. * %NULL.
* *
* Decodes a response to a method call. If the call resulted in an error then * Decodes a response to a method call.
* @error_code is set, otherwise it is %NULL.
* *
* Returns: a new #FlMethodResponse or %NULL on error. * Returns: a new #FlMethodResponse or %NULL on error.
*/ */

View File

@ -5,90 +5,14 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include "flutter/shell/platform/linux/fl_binary_messenger_private.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/fl_platform_handler.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/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/fl_test.h"
#include "flutter/shell/platform/linux/testing/mock_binary_messenger.h"
#include "flutter/testing/testing.h"
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.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<std::string> name,
::testing::Matcher<FlValue*> 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<std::string> name_;
::testing::Matcher<FlValue*> args_;
};
static ::testing::Matcher<GBytes*> MethodCall(
const std::string& name,
::testing::Matcher<FlValue*> 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, G_DECLARE_FINAL_TYPE(FlTestApplication,
fl_test_application, fl_test_application,
FL, FL,
@ -116,26 +40,35 @@ static void fl_test_application_startup(GApplication* application) {
static void fl_test_application_activate(GApplication* application) { static void fl_test_application_activate(GApplication* application) {
G_APPLICATION_CLASS(fl_test_application_parent_class)->activate(application); G_APPLICATION_CLASS(fl_test_application_parent_class)->activate(application);
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlPlatformHandler) handler = fl_platform_handler_new(messenger); g_autoptr(FlPlatformHandler) handler =
fl_platform_handler_new(FL_BINARY_MESSENGER(messenger));
EXPECT_NE(handler, nullptr); 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<FlBinaryMessenger*>(messenger), ::testing::_,
SuccessResponse(exit_result), ::testing::_))
.WillOnce(::testing::Return(true));
// Request app exit. // Request app exit.
gboolean called = FALSE;
g_autoptr(FlValue) args = fl_value_new_map(); g_autoptr(FlValue) args = fl_value_new_map();
fl_value_set_string_take(args, "type", fl_value_new_string("required")); fl_value_set_string_take(args, "type", fl_value_new_string("required"));
g_autoptr(GBytes) message = fl_method_codec_encode_method_call( fl_mock_binary_messenger_invoke_json_method(
FL_METHOD_CODEC(codec), "System.exitApplication", args, nullptr); messenger, "flutter/platform", "System.exitApplication", args,
messenger.ReceiveMessage("flutter/platform", message); [](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
gpointer user_data) {
gboolean* called = static_cast<gboolean*>(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) { static void fl_test_application_dispose(GObject* object) {
@ -171,59 +104,107 @@ FlTestApplication* fl_test_application_new(gboolean* dispose_called) {
} }
TEST(FlPlatformHandlerTest, PlaySound) { TEST(FlPlatformHandlerTest, PlaySound) {
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlPlatformHandler) handler =
g_autoptr(FlPlatformHandler) handler = fl_platform_handler_new(messenger); fl_platform_handler_new(FL_BINARY_MESSENGER(messenger));
EXPECT_NE(handler, nullptr); EXPECT_NE(handler, nullptr);
gboolean called = FALSE;
g_autoptr(FlValue) args = fl_value_new_string("SystemSoundType.alert"); g_autoptr(FlValue) args = fl_value_new_string("SystemSoundType.alert");
g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new(); fl_mock_binary_messenger_invoke_json_method(
g_autoptr(GBytes) message = fl_method_codec_encode_method_call( messenger, "flutter/platform", "SystemSound.play", args,
FL_METHOD_CODEC(codec), "SystemSound.play", args, nullptr); [](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
gpointer user_data) {
gboolean* called = static_cast<gboolean*>(user_data);
*called = TRUE;
g_autoptr(FlValue) null = fl_value_new_null(); EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
EXPECT_CALL(messenger, fl_binary_messenger_send_response(
::testing::Eq<FlBinaryMessenger*>(messenger),
::testing::_, SuccessResponse(null), ::testing::_))
.WillOnce(::testing::Return(true));
messenger.ReceiveMessage("flutter/platform", 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(FlPlatformHandlerTest, ExitApplication) { TEST(FlPlatformHandlerTest, ExitApplication) {
::testing::NiceMock<flutter::testing::MockBinaryMessenger> 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); 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<FlBinaryMessenger*>(messenger),
::testing::_, SuccessResponse(null), ::testing::_))
.WillByDefault(testing::Return(TRUE));
// Indicate that the binding is initialized. // Indicate that the binding is initialized.
g_autoptr(GError) error = nullptr; gboolean called = FALSE;
g_autoptr(GBytes) init_message = fl_method_codec_encode_method_call( fl_mock_binary_messenger_invoke_json_method(
FL_METHOD_CODEC(codec), "System.initializationComplete", nullptr, &error); messenger, "flutter/platform", "System.initializationComplete", nullptr,
messenger.ReceiveMessage("flutter/platform", init_message); [](FlMockBinaryMessenger* messenger, FlMethodResponse* response,
gpointer user_data) {
gboolean* called = static_cast<gboolean*>(user_data);
*called = TRUE;
g_autoptr(FlValue) request_args = fl_value_new_map(); EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response));
fl_value_set_string_take(request_args, "type",
fl_value_new_string("cancelable")); g_autoptr(FlValue) expected_result = fl_value_new_null();
EXPECT_CALL(messenger, EXPECT_TRUE(fl_value_equal(fl_method_success_response_get_result(
fl_binary_messenger_send_on_channel( FL_METHOD_SUCCESS_RESPONSE(response)),
::testing::Eq<FlBinaryMessenger*>(messenger), expected_result));
::testing::StrEq("flutter/platform"), },
MethodCall("System.requestAppExit", FlValueEq(request_args)), &called);
::testing::_, ::testing::_, ::testing::_)); 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<gboolean*>(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(); g_autoptr(FlValue) args = fl_value_new_map();
fl_value_set_string_take(args, "type", fl_value_new_string("cancelable")); fl_value_set_string_take(args, "type", fl_value_new_string("cancelable"));
g_autoptr(GBytes) message = fl_method_codec_encode_method_call( fl_mock_binary_messenger_invoke_json_method(
FL_METHOD_CODEC(codec), "System.exitApplication", args, nullptr); messenger, "flutter/platform", "System.exitApplication", args,
messenger.ReceiveMessage("flutter/platform", message); [](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<GMainLoop*>(user_data));
},
loop);
g_main_loop_run(loop);
EXPECT_TRUE(request_exit_called);
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
} }
TEST(FlPlatformHandlerTest, ExitApplicationDispose) { TEST(FlPlatformHandlerTest, ExitApplicationDispose) {

View File

@ -5,114 +5,187 @@
#include "flutter/shell/platform/linux/fl_settings_handler.h" #include "flutter/shell/platform/linux/fl_settings_handler.h"
#include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.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/fl_engine_private.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h" #include "flutter/shell/platform/linux/testing/fl_mock_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_test.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/shell/platform/linux/testing/mock_settings.h"
#include "flutter/testing/testing.h" #include "flutter/testing/testing.h"
#include "gmock/gmock.h" #include "gmock/gmock.h"
#include "gtest/gtest.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<FlBinaryMessenger*>(messenger), \
::testing::StrEq("flutter/settings"), HasSetting(key, value), \
::testing::A<GCancellable*>(), ::testing::A<GAsyncReadyCallback>(), \
::testing::A<gpointer>()))
TEST(FlSettingsHandlerTest, AlwaysUse24HourFormat) { TEST(FlSettingsHandlerTest, AlwaysUse24HourFormat) {
::testing::NiceMock<flutter::testing::MockSettings> settings; ::testing::NiceMock<flutter::testing::MockSettings> settings;
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine = g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr)); FL_BINARY_MESSENGER(messenger), nullptr));
g_autoptr(FlSettingsHandler) handler = fl_settings_handler_new(engine); 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( EXPECT_CALL(settings, fl_settings_get_clock_format(
::testing::Eq<FlSettings*>(settings))) ::testing::Eq<FlSettings*>(settings)))
.WillOnce(::testing::Return(FL_CLOCK_FORMAT_12H)) .WillOnce(::testing::Return(FL_CLOCK_FORMAT_12H))
.WillOnce(::testing::Return(FL_CLOCK_FORMAT_24H)); .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<gboolean*>(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); fl_settings_handler_start(handler, settings);
EXPECT_TRUE(called);
EXPECT_SETTING(messenger, "alwaysUse24HourFormat", use_24h); called = FALSE;
fl_mock_binary_messenger_set_json_message_channel(
messenger, "flutter/settings",
[](FlMockBinaryMessenger* messenger, FlValue* message,
gpointer user_data) {
gboolean* called = static_cast<gboolean*>(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); fl_settings_emit_changed(settings);
EXPECT_TRUE(called);
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
} }
TEST(FlSettingsHandlerTest, PlatformBrightness) { TEST(FlSettingsHandlerTest, PlatformBrightness) {
::testing::NiceMock<flutter::testing::MockSettings> settings; ::testing::NiceMock<flutter::testing::MockSettings> settings;
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine = g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr)); FL_BINARY_MESSENGER(messenger), nullptr));
g_autoptr(FlSettingsHandler) handler = fl_settings_handler_new(engine); 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( EXPECT_CALL(settings, fl_settings_get_color_scheme(
::testing::Eq<FlSettings*>(settings))) ::testing::Eq<FlSettings*>(settings)))
.WillOnce(::testing::Return(FL_COLOR_SCHEME_LIGHT)) .WillOnce(::testing::Return(FL_COLOR_SCHEME_LIGHT))
.WillOnce(::testing::Return(FL_COLOR_SCHEME_DARK)); .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<gboolean*>(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); fl_settings_handler_start(handler, settings);
EXPECT_TRUE(called);
EXPECT_SETTING(messenger, "platformBrightness", dark); called = FALSE;
fl_mock_binary_messenger_set_json_message_channel(
messenger, "flutter/settings",
[](FlMockBinaryMessenger* messenger, FlValue* message,
gpointer user_data) {
gboolean* called = static_cast<gboolean*>(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); fl_settings_emit_changed(settings);
EXPECT_TRUE(called);
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
} }
TEST(FlSettingsHandlerTest, TextScaleFactor) { TEST(FlSettingsHandlerTest, TextScaleFactor) {
::testing::NiceMock<flutter::testing::MockSettings> settings; ::testing::NiceMock<flutter::testing::MockSettings> settings;
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
g_autoptr(FlEngine) engine = g_autoptr(FlEngine) engine =
FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger", FL_ENGINE(g_object_new(fl_engine_get_type(), "binary-messenger",
FL_BINARY_MESSENGER(messenger), nullptr)); FL_BINARY_MESSENGER(messenger), nullptr));
g_autoptr(FlSettingsHandler) handler = fl_settings_handler_new(engine); 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( EXPECT_CALL(settings, fl_settings_get_text_scaling_factor(
::testing::Eq<FlSettings*>(settings))) ::testing::Eq<FlSettings*>(settings)))
.WillOnce(::testing::Return(1.0)) .WillOnce(::testing::Return(1.0))
.WillOnce(::testing::Return(2.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<gboolean*>(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); fl_settings_handler_start(handler, settings);
EXPECT_TRUE(called);
EXPECT_SETTING(messenger, "textScaleFactor", two); called = FALSE;
fl_mock_binary_messenger_set_json_message_channel(
messenger, "flutter/settings",
[](FlMockBinaryMessenger* messenger, FlValue* message,
gpointer user_data) {
gboolean* called = static_cast<gboolean*>(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); fl_settings_emit_changed(settings);
EXPECT_TRUE(called);
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
} }
// MOCK_ENGINE_PROC is leaky by design // MOCK_ENGINE_PROC is leaky by design

View File

@ -3,254 +3,287 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "flutter/shell/platform/linux/fl_window_state_monitor.h" #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/public/flutter_linux/fl_string_codec.h"
#include "flutter/shell/platform/linux/testing/fl_test.h" #include "flutter/shell/platform/linux/testing/fl_mock_binary_messenger.h"
#include "flutter/shell/platform/linux/testing/mock_binary_messenger.h"
#include "flutter/shell/platform/linux/testing/mock_window.h" #include "flutter/shell/platform/linux/testing/mock_window.h"
#include "gtest/gtest.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<std::string> 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<std::string> value_;
};
::testing::Matcher<GBytes*> LifecycleString(const std::string& value) {
return FlValueStringMatcher(::testing::StrEq(value));
}
TEST(FlWindowStateMonitorTest, GainFocus) { TEST(FlWindowStateMonitorTest, GainFocus) {
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
::testing::NiceMock<flutter::testing::MockWindow> mock_window; ::testing::NiceMock<flutter::testing::MockWindow> mock_window;
gtk_init(0, nullptr); gtk_init(0, nullptr);
EXPECT_CALL(mock_window, gdk_window_get_state) EXPECT_CALL(mock_window, gdk_window_get_state)
.WillOnce(::testing::Return(static_cast<GdkWindowState>(0))); .WillOnce(::testing::Return(static_cast<GdkWindowState>(0)));
EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
::testing::Eq<FlBinaryMessenger*>(messenger), gboolean called = TRUE;
::testing::StrEq("flutter/lifecycle"), fl_mock_binary_messenger_set_string_message_channel(
LifecycleString("AppLifecycleState.resumed"), messenger, "flutter/lifecycle",
::testing::_, ::testing::_, ::testing::_)); [](FlMockBinaryMessenger* messenger, FlValue* message,
gpointer user_data) {
gboolean* called = static_cast<gboolean*>(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)); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
gtk_widget_show(GTK_WIDGET(window)); gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlWindowStateMonitor) monitor = g_autoptr(FlWindowStateMonitor) monitor =
fl_window_state_monitor_new(messenger, window); fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window);
GdkEvent event = { GdkEvent event = {
.window_state = {.new_window_state = GDK_WINDOW_STATE_FOCUSED}}; .window_state = {.new_window_state = GDK_WINDOW_STATE_FOCUSED}};
gboolean handled; gboolean handled;
g_signal_emit_by_name(window, "window-state-event", &event, &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) { TEST(FlWindowStateMonitorTest, LoseFocus) {
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
::testing::NiceMock<flutter::testing::MockWindow> mock_window; ::testing::NiceMock<flutter::testing::MockWindow> mock_window;
gtk_init(0, nullptr); gtk_init(0, nullptr);
EXPECT_CALL(mock_window, gdk_window_get_state) EXPECT_CALL(mock_window, gdk_window_get_state)
.WillOnce(::testing::Return(GDK_WINDOW_STATE_FOCUSED)); .WillOnce(::testing::Return(GDK_WINDOW_STATE_FOCUSED));
EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( gboolean called = TRUE;
::testing::Eq<FlBinaryMessenger*>(messenger), fl_mock_binary_messenger_set_string_message_channel(
::testing::StrEq("flutter/lifecycle"), messenger, "flutter/lifecycle",
LifecycleString("AppLifecycleState.inactive"), [](FlMockBinaryMessenger* messenger, FlValue* message,
::testing::_, ::testing::_, ::testing::_)); gpointer user_data) {
gboolean* called = static_cast<gboolean*>(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)); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
gtk_widget_show(GTK_WIDGET(window)); gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlWindowStateMonitor) monitor = g_autoptr(FlWindowStateMonitor) monitor =
fl_window_state_monitor_new(messenger, window); fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window);
GdkEvent event = { GdkEvent event = {
.window_state = {.new_window_state = static_cast<GdkWindowState>(0)}}; .window_state = {.new_window_state = static_cast<GdkWindowState>(0)}};
gboolean handled; gboolean handled;
g_signal_emit_by_name(window, "window-state-event", &event, &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) { TEST(FlWindowStateMonitorTest, EnterIconified) {
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
::testing::NiceMock<flutter::testing::MockWindow> mock_window; ::testing::NiceMock<flutter::testing::MockWindow> mock_window;
gtk_init(0, nullptr); gtk_init(0, nullptr);
EXPECT_CALL(mock_window, gdk_window_get_state) EXPECT_CALL(mock_window, gdk_window_get_state)
.WillOnce(::testing::Return(static_cast<GdkWindowState>(0))); .WillOnce(::testing::Return(static_cast<GdkWindowState>(0)));
EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( gboolean called = TRUE;
::testing::Eq<FlBinaryMessenger*>(messenger), fl_mock_binary_messenger_set_string_message_channel(
::testing::StrEq("flutter/lifecycle"), messenger, "flutter/lifecycle",
LifecycleString("AppLifecycleState.hidden"), [](FlMockBinaryMessenger* messenger, FlValue* message,
::testing::_, ::testing::_, ::testing::_)); gpointer user_data) {
gboolean* called = static_cast<gboolean*>(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)); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
gtk_widget_show(GTK_WIDGET(window)); gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlWindowStateMonitor) monitor = g_autoptr(FlWindowStateMonitor) monitor =
fl_window_state_monitor_new(messenger, window); fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window);
GdkEvent event = { GdkEvent event = {
.window_state = {.new_window_state = GDK_WINDOW_STATE_ICONIFIED}}; .window_state = {.new_window_state = GDK_WINDOW_STATE_ICONIFIED}};
gboolean handled; gboolean handled;
g_signal_emit_by_name(window, "window-state-event", &event, &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) { TEST(FlWindowStateMonitorTest, LeaveIconified) {
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
::testing::NiceMock<flutter::testing::MockWindow> mock_window; ::testing::NiceMock<flutter::testing::MockWindow> mock_window;
gtk_init(0, nullptr); gtk_init(0, nullptr);
EXPECT_CALL(mock_window, gdk_window_get_state) EXPECT_CALL(mock_window, gdk_window_get_state)
.WillOnce(::testing::Return(GDK_WINDOW_STATE_ICONIFIED)); .WillOnce(::testing::Return(GDK_WINDOW_STATE_ICONIFIED));
EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( gboolean called = TRUE;
::testing::Eq<FlBinaryMessenger*>(messenger), fl_mock_binary_messenger_set_string_message_channel(
::testing::StrEq("flutter/lifecycle"), messenger, "flutter/lifecycle",
LifecycleString("AppLifecycleState.inactive"), [](FlMockBinaryMessenger* messenger, FlValue* message,
::testing::_, ::testing::_, ::testing::_)); gpointer user_data) {
gboolean* called = static_cast<gboolean*>(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)); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
gtk_widget_show(GTK_WIDGET(window)); gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlWindowStateMonitor) monitor = g_autoptr(FlWindowStateMonitor) monitor =
fl_window_state_monitor_new(messenger, window); fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window);
GdkEvent event = { GdkEvent event = {
.window_state = {.new_window_state = static_cast<GdkWindowState>(0)}}; .window_state = {.new_window_state = static_cast<GdkWindowState>(0)}};
gboolean handled; gboolean handled;
g_signal_emit_by_name(window, "window-state-event", &event, &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) { TEST(FlWindowStateMonitorTest, LeaveIconifiedFocused) {
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
::testing::NiceMock<flutter::testing::MockWindow> mock_window; ::testing::NiceMock<flutter::testing::MockWindow> mock_window;
gtk_init(0, nullptr); gtk_init(0, nullptr);
EXPECT_CALL(mock_window, gdk_window_get_state) EXPECT_CALL(mock_window, gdk_window_get_state)
.WillOnce(::testing::Return(GDK_WINDOW_STATE_ICONIFIED)); .WillOnce(::testing::Return(GDK_WINDOW_STATE_ICONIFIED));
EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( gboolean called = TRUE;
::testing::Eq<FlBinaryMessenger*>(messenger), fl_mock_binary_messenger_set_string_message_channel(
::testing::StrEq("flutter/lifecycle"), messenger, "flutter/lifecycle",
LifecycleString("AppLifecycleState.resumed"), [](FlMockBinaryMessenger* messenger, FlValue* message,
::testing::_, ::testing::_, ::testing::_)); gpointer user_data) {
gboolean* called = static_cast<gboolean*>(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)); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
gtk_widget_show(GTK_WIDGET(window)); gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlWindowStateMonitor) monitor = g_autoptr(FlWindowStateMonitor) monitor =
fl_window_state_monitor_new(messenger, window); fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window);
GdkEvent event = { GdkEvent event = {
.window_state = {.new_window_state = static_cast<GdkWindowState>( .window_state = {.new_window_state = static_cast<GdkWindowState>(
GDK_WINDOW_STATE_FOCUSED)}}; GDK_WINDOW_STATE_FOCUSED)}};
gboolean handled; gboolean handled;
g_signal_emit_by_name(window, "window-state-event", &event, &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) { TEST(FlWindowStateMonitorTest, EnterWithdrawn) {
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
::testing::NiceMock<flutter::testing::MockWindow> mock_window; ::testing::NiceMock<flutter::testing::MockWindow> mock_window;
gtk_init(0, nullptr); gtk_init(0, nullptr);
EXPECT_CALL(mock_window, gdk_window_get_state) EXPECT_CALL(mock_window, gdk_window_get_state)
.WillOnce(::testing::Return(static_cast<GdkWindowState>(0))); .WillOnce(::testing::Return(static_cast<GdkWindowState>(0)));
EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( gboolean called = TRUE;
::testing::Eq<FlBinaryMessenger*>(messenger), fl_mock_binary_messenger_set_string_message_channel(
::testing::StrEq("flutter/lifecycle"), messenger, "flutter/lifecycle",
LifecycleString("AppLifecycleState.hidden"), [](FlMockBinaryMessenger* messenger, FlValue* message,
::testing::_, ::testing::_, ::testing::_)); gpointer user_data) {
gboolean* called = static_cast<gboolean*>(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)); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
gtk_widget_show(GTK_WIDGET(window)); gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlWindowStateMonitor) monitor = g_autoptr(FlWindowStateMonitor) monitor =
fl_window_state_monitor_new(messenger, window); fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window);
GdkEvent event = { GdkEvent event = {
.window_state = {.new_window_state = GDK_WINDOW_STATE_WITHDRAWN}}; .window_state = {.new_window_state = GDK_WINDOW_STATE_WITHDRAWN}};
gboolean handled; gboolean handled;
g_signal_emit_by_name(window, "window-state-event", &event, &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) { TEST(FlWindowStateMonitorTest, LeaveWithdrawn) {
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
::testing::NiceMock<flutter::testing::MockWindow> mock_window; ::testing::NiceMock<flutter::testing::MockWindow> mock_window;
gtk_init(0, nullptr); gtk_init(0, nullptr);
EXPECT_CALL(mock_window, gdk_window_get_state) EXPECT_CALL(mock_window, gdk_window_get_state)
.WillOnce(::testing::Return(GDK_WINDOW_STATE_WITHDRAWN)); .WillOnce(::testing::Return(GDK_WINDOW_STATE_WITHDRAWN));
EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( gboolean called = TRUE;
::testing::Eq<FlBinaryMessenger*>(messenger), fl_mock_binary_messenger_set_string_message_channel(
::testing::StrEq("flutter/lifecycle"), messenger, "flutter/lifecycle",
LifecycleString("AppLifecycleState.inactive"), [](FlMockBinaryMessenger* messenger, FlValue* message,
::testing::_, ::testing::_, ::testing::_)); gpointer user_data) {
gboolean* called = static_cast<gboolean*>(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)); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
gtk_widget_show(GTK_WIDGET(window)); gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlWindowStateMonitor) monitor = g_autoptr(FlWindowStateMonitor) monitor =
fl_window_state_monitor_new(messenger, window); fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window);
GdkEvent event = { GdkEvent event = {
.window_state = {.new_window_state = static_cast<GdkWindowState>(0)}}; .window_state = {.new_window_state = static_cast<GdkWindowState>(0)}};
gboolean handled; gboolean handled;
g_signal_emit_by_name(window, "window-state-event", &event, &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) { TEST(FlWindowStateMonitorTest, LeaveWithdrawnFocused) {
::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger; g_autoptr(FlMockBinaryMessenger) messenger = fl_mock_binary_messenger_new();
::testing::NiceMock<flutter::testing::MockWindow> mock_window; ::testing::NiceMock<flutter::testing::MockWindow> mock_window;
gtk_init(0, nullptr); gtk_init(0, nullptr);
EXPECT_CALL(mock_window, gdk_window_get_state) EXPECT_CALL(mock_window, gdk_window_get_state)
.WillOnce(::testing::Return(GDK_WINDOW_STATE_WITHDRAWN)); .WillOnce(::testing::Return(GDK_WINDOW_STATE_WITHDRAWN));
EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel( gboolean called = TRUE;
::testing::Eq<FlBinaryMessenger*>(messenger), fl_mock_binary_messenger_set_string_message_channel(
::testing::StrEq("flutter/lifecycle"), messenger, "flutter/lifecycle",
LifecycleString("AppLifecycleState.resumed"), [](FlMockBinaryMessenger* messenger, FlValue* message,
::testing::_, ::testing::_, ::testing::_)); gpointer user_data) {
gboolean* called = static_cast<gboolean*>(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)); GtkWindow* window = GTK_WINDOW(gtk_window_new(GTK_WINDOW_TOPLEVEL));
gtk_widget_show(GTK_WIDGET(window)); gtk_widget_show(GTK_WIDGET(window));
g_autoptr(FlWindowStateMonitor) monitor = g_autoptr(FlWindowStateMonitor) monitor =
fl_window_state_monitor_new(messenger, window); fl_window_state_monitor_new(FL_BINARY_MESSENGER(messenger), window);
GdkEvent event = { GdkEvent event = {
.window_state = {.new_window_state = static_cast<GdkWindowState>( .window_state = {.new_window_state = static_cast<GdkWindowState>(
GDK_WINDOW_STATE_FOCUSED)}}; GDK_WINDOW_STATE_FOCUSED)}};
gboolean handled; gboolean handled;
g_signal_emit_by_name(window, "window-state-event", &event, &handled); g_signal_emit_by_name(window, "window-state-event", &event, &handled);
EXPECT_TRUE(called);
fl_binary_messenger_shutdown(FL_BINARY_MESSENGER(messenger));
} }

View File

@ -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<MockChannel*>(
g_hash_table_lookup(self->mock_channels, channel));
MockMessageChannel* mock_message_channel = static_cast<MockMessageChannel*>(
g_hash_table_lookup(self->mock_message_channels, channel));
MockMethodChannel* mock_method_channel = static_cast<MockMethodChannel*>(
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<GDestroyNotify>(g_bytes_unref));
}
static GBytes* fl_mock_binary_messenger_send_on_channel_finish(
FlBinaryMessenger* messenger,
GAsyncResult* result,
GError** error) {
return static_cast<GBytes*>(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<Handler*>(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<SendMessageData*>(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<InvokeMethodData*>(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);
}

View File

@ -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_

View File

@ -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) {}

View File

@ -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 <unordered_map>
#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<std::string, FlBinaryMessengerMessageHandler>
message_handlers_;
std::unordered_map<std::string, FlBinaryMessengerResponseHandle*>
response_handles_;
std::unordered_map<std::string, gpointer> user_datas_;
};
} // namespace testing
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_LINUX_TESTING_MOCK_BINARY_MESSENGER_H_

View File

@ -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));
}

View File

@ -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_