Skip to content

Commit

Permalink
Switch Linux embedding to proc table embedder API (#22280)
Browse files Browse the repository at this point in the history
Switches the Linux embedding from the standard C API to the new proctable version, to allow for unit testing of the embedding layer separately from the embedder APIs implementation.
  • Loading branch information
stuartmorgan committed Nov 4, 2020
1 parent d6ac456 commit bf25922
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 52 deletions.
17 changes: 14 additions & 3 deletions shell/platform/linux/BUILD.gn
Expand Up @@ -119,7 +119,10 @@ source_set("flutter_linux_sources") {
]

# Set flag to stop headers being directly included (library users should not do this)
defines = [ "FLUTTER_LINUX_COMPILATION" ]
defines = [
"FLUTTER_LINUX_COMPILATION",
"FLUTTER_ENGINE_NO_PROTOTYPES",
]

deps = [
"//flutter/shell/platform/common/cpp:common_cpp_input",
Expand All @@ -138,6 +141,8 @@ source_set("flutter_linux") {
"//third_party/khronos:khronos_headers",
]

defines = [ "FLUTTER_ENGINE_NO_PROTOTYPES" ]

public_deps = [ ":flutter_linux_sources" ]

deps = [ "//flutter/shell/platform/embedder:embedder_as_internal_library" ]
Expand Down Expand Up @@ -182,14 +187,20 @@ executable("flutter_linux_unittests") {
"//flutter/shell/platform/linux/config:x11",
]

# Set flag to allow public headers to be directly included (library users should not do this)
defines = [ "FLUTTER_LINUX_COMPILATION" ]
defines = [
"FLUTTER_ENGINE_NO_PROTOTYPES",

# Set flag to allow public headers to be directly included
# (library users should not do this)
"FLUTTER_LINUX_COMPILATION",
]

deps = [
":flutter_linux_fixtures",
":flutter_linux_sources",
"//flutter/runtime:libdart",
"//flutter/shell/platform/embedder:embedder_headers",
"//flutter/shell/platform/embedder:embedder_test_utils",
"//flutter/testing",
]
}
Expand Down
47 changes: 44 additions & 3 deletions shell/platform/linux/fl_basic_message_channel_test.cc
Expand Up @@ -5,13 +5,54 @@
// Included first as it collides with the X11 headers.
#include "gtest/gtest.h"

#include "flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h"

#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
#include "flutter/shell/platform/linux/fl_binary_messenger_private.h"
#include "flutter/shell/platform/linux/fl_engine_private.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_message_codec.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h"
#include "flutter/shell/platform/linux/testing/fl_test.h"
#include "flutter/shell/platform/linux/testing/mock_renderer.h"

// Checks sending a message without a reponse works.
TEST(FlBasicMessageChannelTest, SendMessageWithoutResponse) {
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);

g_autoptr(FlEngine) engine = make_mock_engine();
FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);

bool called = false;
embedder_api->SendPlatformMessage = MOCK_ENGINE_PROC(
SendPlatformMessage,
([&called](auto engine, const FlutterPlatformMessage* message) {
called = true;
EXPECT_STREQ(message->channel, "test");
EXPECT_EQ(message->response_handle, nullptr);

g_autoptr(GBytes) message_bytes =
g_bytes_new(message->message, message->message_size);
g_autoptr(FlStandardMessageCodec) codec =
fl_standard_message_codec_new();
FlValue* message_value = fl_message_codec_decode_message(
FL_MESSAGE_CODEC(codec), message_bytes, nullptr);
EXPECT_EQ(fl_value_get_type(message_value), FL_VALUE_TYPE_STRING);
EXPECT_STREQ(fl_value_get_string(message_value), "Hello World!");

return kSuccess;
}));

FlBinaryMessenger* messenger = fl_binary_messenger_new(engine);
g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new();
g_autoptr(FlBasicMessageChannel) channel =
fl_basic_message_channel_new(messenger, "test", FL_MESSAGE_CODEC(codec));
g_autoptr(FlValue) message = fl_value_new_string("Hello World!");
fl_basic_message_channel_send(channel, message, nullptr, nullptr, loop);

EXPECT_TRUE(called);
}

// Called when the message response is received in the SendMessage test.
static void echo_response_cb(GObject* object,
GAsyncResult* result,
Expand All @@ -28,8 +69,8 @@ static void echo_response_cb(GObject* object,
g_main_loop_quit(static_cast<GMainLoop*>(user_data));
}

// Checks sending a message works.
TEST(FlBasicMessageChannelTest, SendMessage) {
// Checks sending a message with a response works.
TEST(FlBasicMessageChannelTest, SendMessageWithResponse) {
g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);

g_autoptr(FlEngine) engine = make_mock_engine();
Expand Down
43 changes: 27 additions & 16 deletions shell/platform/linux/fl_engine.cc
Expand Up @@ -32,6 +32,7 @@ struct _FlEngine {
FlBinaryMessenger* binary_messenger;
FlutterEngineAOTData aot_data;
FLUTTER_API_SYMBOL(FlutterEngine) engine;
FlutterEngineProcTable embedder_api;

// Function to call when a platform message is received.
FlEnginePlatformMessageHandler platform_message_handler;
Expand Down Expand Up @@ -127,7 +128,7 @@ static void setup_locales(FlEngine* self) {
}
FlutterLocale** locales =
reinterpret_cast<FlutterLocale**>(locales_array->pdata);
FlutterEngineResult result = FlutterEngineUpdateLocales(
FlutterEngineResult result = self->embedder_api.UpdateLocales(
self->engine, const_cast<const FlutterLocale**>(locales),
locales_array->len);
if (result != kSuccess) {
Expand All @@ -143,7 +144,7 @@ static gboolean flutter_source_dispatch(GSource* source,
FlEngine* self = fl_source->engine;

FlutterEngineResult result =
FlutterEngineRunTask(self->engine, &fl_source->task);
self->embedder_api.RunTask(self->engine, &fl_source->task);
if (result != kSuccess) {
g_warning("Failed to run Flutter task\n");
}
Expand Down Expand Up @@ -302,12 +303,12 @@ static void fl_engine_dispose(GObject* object) {
FlEngine* self = FL_ENGINE(object);

if (self->engine != nullptr) {
FlutterEngineShutdown(self->engine);
self->embedder_api.Shutdown(self->engine);
self->engine = nullptr;
}

if (self->aot_data != nullptr) {
FlutterEngineCollectAOTData(self->aot_data);
self->embedder_api.CollectAOTData(self->aot_data);
self->aot_data = nullptr;
}

Expand All @@ -332,6 +333,9 @@ static void fl_engine_class_init(FlEngineClass* klass) {
static void fl_engine_init(FlEngine* self) {
self->thread = g_thread_self();

self->embedder_api.struct_size = sizeof(FlutterEngineProcTable);
FlutterEngineGetProcAddresses(&self->embedder_api);

self->binary_messenger = fl_binary_messenger_new(self);
}

Expand Down Expand Up @@ -400,27 +404,28 @@ gboolean fl_engine_start(FlEngine* self, GError** error) {
args.dart_entrypoint_argv =
reinterpret_cast<const char* const*>(dart_entrypoint_args);

if (FlutterEngineRunsAOTCompiledDartCode()) {
if (self->embedder_api.RunsAOTCompiledDartCode()) {
FlutterEngineAOTDataSource source = {};
source.type = kFlutterEngineAOTDataSourceTypeElfPath;
source.elf_path = fl_dart_project_get_aot_library_path(self->project);
if (FlutterEngineCreateAOTData(&source, &self->aot_data) != kSuccess) {
if (self->embedder_api.CreateAOTData(&source, &self->aot_data) !=
kSuccess) {
g_set_error(error, fl_engine_error_quark(), FL_ENGINE_ERROR_FAILED,
"Failed to create AOT data");
return FALSE;
}
args.aot_data = self->aot_data;
}

FlutterEngineResult result = FlutterEngineInitialize(
FlutterEngineResult result = self->embedder_api.Initialize(
FLUTTER_ENGINE_VERSION, &config, &args, self, &self->engine);
if (result != kSuccess) {
g_set_error(error, fl_engine_error_quark(), FL_ENGINE_ERROR_FAILED,
"Failed to initialize Flutter engine");
return FALSE;
}

result = FlutterEngineRunInitialized(self->engine);
result = self->embedder_api.RunInitialized(self->engine);
if (result != kSuccess) {
g_set_error(error, fl_engine_error_quark(), FL_ENGINE_ERROR_FAILED,
"Failed to run Flutter engine");
Expand All @@ -432,6 +437,10 @@ gboolean fl_engine_start(FlEngine* self, GError** error) {
return TRUE;
}

FlutterEngineProcTable* fl_engine_get_embedder_api(FlEngine* self) {
return &(self->embedder_api);
}

void fl_engine_set_platform_message_handler(
FlEngine* self,
FlEnginePlatformMessageHandler handler,
Expand Down Expand Up @@ -470,7 +479,7 @@ gboolean fl_engine_send_platform_message_response(
data =
static_cast<const uint8_t*>(g_bytes_get_data(response, &data_length));
}
FlutterEngineResult result = FlutterEngineSendPlatformMessageResponse(
FlutterEngineResult result = self->embedder_api.SendPlatformMessageResponse(
self->engine, handle, data, data_length);

if (result != kSuccess) {
Expand Down Expand Up @@ -501,9 +510,10 @@ void fl_engine_send_platform_message(FlEngine* self,
return;
}

FlutterEngineResult result = FlutterPlatformMessageCreateResponseHandle(
self->engine, fl_engine_platform_message_response_cb, task,
&response_handle);
FlutterEngineResult result =
self->embedder_api.PlatformMessageCreateResponseHandle(
self->engine, fl_engine_platform_message_response_cb, task,
&response_handle);
if (result != kSuccess) {
g_task_return_new_error(task, fl_engine_error_quark(),
FL_ENGINE_ERROR_FAILED,
Expand All @@ -525,7 +535,7 @@ void fl_engine_send_platform_message(FlEngine* self,
fl_message.message_size = message != nullptr ? g_bytes_get_size(message) : 0;
fl_message.response_handle = response_handle;
FlutterEngineResult result =
FlutterEngineSendPlatformMessage(self->engine, &fl_message);
self->embedder_api.SendPlatformMessage(self->engine, &fl_message);

if (result != kSuccess && task != nullptr) {
g_task_return_new_error(task, fl_engine_error_quark(),
Expand All @@ -535,7 +545,8 @@ void fl_engine_send_platform_message(FlEngine* self,
}

if (response_handle != nullptr) {
FlutterPlatformMessageReleaseResponseHandle(self->engine, response_handle);
self->embedder_api.PlatformMessageReleaseResponseHandle(self->engine,
response_handle);
}
}

Expand Down Expand Up @@ -563,7 +574,7 @@ void fl_engine_send_window_metrics_event(FlEngine* self,
event.width = width;
event.height = height;
event.pixel_ratio = pixel_ratio;
FlutterEngineSendWindowMetricsEvent(self->engine, &event);
self->embedder_api.SendWindowMetricsEvent(self->engine, &event);
}

void fl_engine_send_mouse_pointer_event(FlEngine* self,
Expand Down Expand Up @@ -593,7 +604,7 @@ void fl_engine_send_mouse_pointer_event(FlEngine* self,
fl_event.scroll_delta_y = scroll_delta_y;
fl_event.device_kind = kFlutterPointerDeviceKindMouse;
fl_event.buttons = buttons;
FlutterEngineSendPointerEvent(self->engine, &fl_event, 1);
self->embedder_api.SendPointerEvent(self->engine, &fl_event, 1);
}

G_MODULE_EXPORT FlBinaryMessenger* fl_engine_get_binary_messenger(
Expand Down
10 changes: 10 additions & 0 deletions shell/platform/linux/fl_engine_private.h
Expand Up @@ -55,6 +55,16 @@ typedef gboolean (*FlEnginePlatformMessageHandler)(
*/
FlEngine* fl_engine_new(FlDartProject* project, FlRenderer* renderer);

/**
* fl_engine_get_embedder_api:
* @engine: an #FlEngine.
*
* Gets the embedder API proc table, allowing modificiations for unit testing.
*
* Returns: a mutable pointer to the embedder API proc table.
*/
FlutterEngineProcTable* fl_engine_get_embedder_api(FlEngine* engine);

/**
* fl_engine_set_platform_message_handler:
* @engine: an #FlEngine.
Expand Down

0 comments on commit bf25922

Please sign in to comment.