Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions shell/platform/linux/fl_method_channel.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ static void channel_closed_cb(gpointer user_data) {
g_autoptr(FlMethodChannel) self = FL_METHOD_CHANNEL(user_data);

self->channel_closed = TRUE;
// Clear the messenger so that disposing the channel will not clear the
// messenger's mapped channel, since `channel_closed_cb` means the messenger
// has abandoned this channel.
self->messenger = nullptr;

// Disconnect handler.
if (self->method_call_handler_destroy_notify != nullptr) {
Expand Down
141 changes: 141 additions & 0 deletions shell/platform/linux/fl_method_channel_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -603,3 +603,144 @@ TEST(FlMethodChannelTest, ReceiveMethodCallRespondErrorError) {
// Blocks here until method_call_error_error_cb is called.
g_main_loop_run(loop);
}

struct UserDataReassignMethod {
GMainLoop* loop;
int count;
};

// This callback parses the user data as UserDataReassignMethod,
// increases its `count`, and quits `loop`.
static void reassign_method_cb(FlMethodChannel* channel,
FlMethodCall* method_call,
gpointer raw_user_data) {
UserDataReassignMethod* user_data =
static_cast<UserDataReassignMethod*>(raw_user_data);
user_data->count += 1;

g_autoptr(FlValue) result = fl_value_new_string("Polo!");
g_autoptr(GError) error = nullptr;
EXPECT_TRUE(fl_method_call_respond_success(method_call, result, &error));
EXPECT_EQ(error, nullptr);

g_main_loop_quit(user_data->loop);
}

// Make sure that the following steps will work properly:
//
// 1. Register a method channel.
// 2. Dispose the method channel, and it's unregistered.
// 3. Register a new channel with the same name.
//
// This is a regression test to https://github.com/flutter/flutter/issues/90817.
TEST(FlMethodChannelTest, ReplaceADisposedMethodChannel) {
const char* method_name = "test/standard-method";
// The loop is used to pause the main process until the callback is fully
// executed.
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
g_autoptr(FlEngine) engine = make_mock_engine();
FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();

g_autoptr(FlValue) args = fl_value_new_list();
fl_value_append_take(args, fl_value_new_string(method_name));
fl_value_append_take(args, fl_value_new_string("FOO"));
fl_value_append_take(args, fl_value_new_string("BAR"));

// Register the first channel and test if it works.
UserDataReassignMethod user_data1{
.loop = loop,
.count = 100,
};
FlMethodChannel* channel1 =
fl_method_channel_new(messenger, method_name, FL_METHOD_CODEC(codec));
fl_method_channel_set_method_call_handler(channel1, reassign_method_cb,
&user_data1, nullptr);

fl_method_channel_invoke_method(channel1, "InvokeMethod", args, nullptr,
nullptr, nullptr);
g_main_loop_run(loop);
EXPECT_EQ(user_data1.count, 101);

// Dispose the first channel.
g_object_unref(channel1);

// Register the second channel and test if it works.
UserDataReassignMethod user_data2{
.loop = loop,
.count = 100,
};
g_autoptr(FlMethodChannel) channel2 =
fl_method_channel_new(messenger, method_name, FL_METHOD_CODEC(codec));
fl_method_channel_set_method_call_handler(channel2, reassign_method_cb,
&user_data2, nullptr);

fl_method_channel_invoke_method(channel2, "InvokeMethod", args, nullptr,
nullptr, nullptr);
g_main_loop_run(loop);

EXPECT_EQ(user_data1.count, 101);
EXPECT_EQ(user_data2.count, 101);
}

// Make sure that the following steps will work properly:
//
// 1. Register a method channel.
// 2. Register the same name with a new channel.
// 3. Dispose the previous method channel.
//
// This is a regression test to https://github.com/flutter/flutter/issues/90817.
TEST(FlMethodChannelTest, DisposeAReplacedMethodChannel) {
const char* method_name = "test/standard-method";
// The loop is used to pause the main process until the callback is fully
// executed.
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
g_autoptr(FlEngine) engine = make_mock_engine();
FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();

g_autoptr(FlValue) args = fl_value_new_list();
fl_value_append_take(args, fl_value_new_string(method_name));
fl_value_append_take(args, fl_value_new_string("FOO"));
fl_value_append_take(args, fl_value_new_string("BAR"));

// Register the first channel and test if it works.
UserDataReassignMethod user_data1{
.loop = loop,
.count = 100,
};
FlMethodChannel* channel1 =
fl_method_channel_new(messenger, method_name, FL_METHOD_CODEC(codec));
fl_method_channel_set_method_call_handler(channel1, reassign_method_cb,
&user_data1, nullptr);

fl_method_channel_invoke_method(channel1, "InvokeMethod", args, nullptr,
nullptr, nullptr);
g_main_loop_run(loop);
EXPECT_EQ(user_data1.count, 101);

// Register a new channel to the same name.
UserDataReassignMethod user_data2{
.loop = loop,
.count = 100,
};
g_autoptr(FlMethodChannel) channel2 =
fl_method_channel_new(messenger, method_name, FL_METHOD_CODEC(codec));
fl_method_channel_set_method_call_handler(channel2, reassign_method_cb,
&user_data2, nullptr);

fl_method_channel_invoke_method(channel2, "InvokeMethod", args, nullptr,
nullptr, nullptr);
g_main_loop_run(loop);
EXPECT_EQ(user_data1.count, 101);
EXPECT_EQ(user_data2.count, 101);

// Dispose the first channel. The new channel should keep working.
g_object_unref(channel1);

fl_method_channel_invoke_method(channel2, "InvokeMethod", args, nullptr,
nullptr, nullptr);
g_main_loop_run(loop);
EXPECT_EQ(user_data1.count, 101);
EXPECT_EQ(user_data2.count, 102);
}