diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index ba413f5181a0adc1320b345b91c749f7c1a5ca9a..9cde267ecc4e69730c39aa1fb011c0b52cb80761 100755 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1191,10 +1191,12 @@ FILE: ../../../flutter/shell/platform/glfw/public/flutter_glfw.h FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.cc FILE: ../../../flutter/shell/platform/glfw/text_input_plugin.h FILE: ../../../flutter/shell/platform/linux/fl_basic_message_channel.cc +FILE: ../../../flutter/shell/platform/linux/fl_basic_message_channel_test.cc FILE: ../../../flutter/shell/platform/linux/fl_binary_codec.cc FILE: ../../../flutter/shell/platform/linux/fl_binary_codec_test.cc FILE: ../../../flutter/shell/platform/linux/fl_binary_messenger.cc FILE: ../../../flutter/shell/platform/linux/fl_binary_messenger_private.h +FILE: ../../../flutter/shell/platform/linux/fl_binary_messenger_test.cc FILE: ../../../flutter/shell/platform/linux/fl_dart_project.cc FILE: ../../../flutter/shell/platform/linux/fl_dart_project_test.cc FILE: ../../../flutter/shell/platform/linux/fl_engine.cc @@ -1211,6 +1213,7 @@ FILE: ../../../flutter/shell/platform/linux/fl_method_call.cc FILE: ../../../flutter/shell/platform/linux/fl_method_call_private.h FILE: ../../../flutter/shell/platform/linux/fl_method_channel.cc FILE: ../../../flutter/shell/platform/linux/fl_method_channel_private.h +FILE: ../../../flutter/shell/platform/linux/fl_method_channel_test.cc FILE: ../../../flutter/shell/platform/linux/fl_method_codec.cc FILE: ../../../flutter/shell/platform/linux/fl_method_codec_private.h FILE: ../../../flutter/shell/platform/linux/fl_method_codec_test.cc diff --git a/shell/platform/embedder/BUILD.gn b/shell/platform/embedder/BUILD.gn index f4b5be57e13eb79f71b9ccb69835027e6ce30380..609337164c8a4552d67d3f6fecea5fcac1a8788e 100644 --- a/shell/platform/embedder/BUILD.gn +++ b/shell/platform/embedder/BUILD.gn @@ -24,7 +24,6 @@ template("embedder_source_set") { source_set(target_name) { sources = [ "embedder.cc", - "embedder.h", "embedder_engine.cc", "embedder_engine.h", "embedder_external_texture_gl.cc", @@ -75,6 +74,10 @@ template("embedder_source_set") { "//third_party/skia", ] + public_deps = [ + ":embedder_headers", + ] + public_configs += [ "//flutter:config" ] } } @@ -87,6 +90,14 @@ embedder_source_set("embedder_with_symbol_prefix") { public_configs = [ ":embedder_prefix_config" ] } +source_set("embedder_headers") { + public = [ + "embedder.h", + ] + + public_configs = [ "//flutter:config" ] +} + config("embedder_prefix_config") { defines = [ "FLUTTER_API_SYMBOL_PREFIX=Embedder" ] } diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 809d5a25d849a77d8fc51f0f9334c2bf6924dd32..54c550d044bc74de32f279294cd6bbee0ea4f46b 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -70,9 +70,11 @@ config("relative_flutter_linux_headers") { include_dirs = [ "public" ] } -source_set("flutter_linux") { +source_set("flutter_linux_sources") { public = _public_headers + configs += [ "//flutter/shell/platform/linux/config:gtk" ] + sources = [ "fl_basic_message_channel.cc", "fl_binary_codec.cc", @@ -99,19 +101,29 @@ source_set("flutter_linux") { "fl_view.cc", ] + # Set flag to stop headers being directly included (library users should not do this) + defines = [ "FLUTTER_LINUX_COMPILATION" ] + + deps = [ + "//flutter/shell/platform/embedder:embedder_headers", + "//third_party/rapidjson", + ] +} + +source_set("flutter_linux") { configs += [ "//flutter/shell/platform/linux/config:gtk", "//flutter/shell/platform/linux/config:egl", "//third_party/khronos:khronos_headers", ] - # Set flag to stop headers being directly included (library users should not do this) - defines = [ "FLUTTER_LINUX_COMPILATION" ] + public_deps = [ + ":flutter_linux_sources", + ] deps = [ "//flutter/shell/platform/common/cpp:common_cpp_input", "//flutter/shell/platform/embedder:embedder_with_symbol_prefix", - "//third_party/rapidjson", ] } @@ -123,11 +135,14 @@ executable("flutter_linux_unittests") { testonly = true sources = [ + "fl_basic_message_channel_test.cc", "fl_binary_codec_test.cc", + "fl_binary_messenger_test.cc", "fl_dart_project_test.cc", "fl_json_message_codec_test.cc", "fl_json_method_codec_test.cc", "fl_message_codec_test.cc", + "fl_method_channel_test.cc", "fl_method_codec_test.cc", "fl_method_response_test.cc", "fl_standard_message_codec_test.cc", @@ -135,6 +150,9 @@ executable("flutter_linux_unittests") { "fl_string_codec_test.cc", "fl_value_test.cc", "testing/fl_test.cc", + "testing/mock_egl.cc", + "testing/mock_engine.cc", + "testing/mock_renderer.cc", ] public_configs = [ "//flutter:config" ] @@ -145,8 +163,10 @@ executable("flutter_linux_unittests") { defines = [ "FLUTTER_LINUX_COMPILATION" ] deps = [ - ":flutter_linux", ":flutter_linux_fixtures", + ":flutter_linux_sources", + "//flutter/runtime:libdart", + "//flutter/shell/platform/embedder:embedder_headers", "//flutter/testing", ] } diff --git a/shell/platform/linux/fl_basic_message_channel_test.cc b/shell/platform/linux/fl_basic_message_channel_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..002ea0e42cfa0f75c82cf2338529fde4791c20ae --- /dev/null +++ b/shell/platform/linux/fl_basic_message_channel_test.cc @@ -0,0 +1,152 @@ +// 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. + +// Included first as it collides with the X11 headers. +#include "gtest/gtest.h" + +#include "flutter/shell/platform/linux/fl_binary_messenger_private.h" +#include "flutter/shell/platform/linux/fl_engine_private.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_message_codec.h" +#include "flutter/shell/platform/linux/testing/mock_renderer.h" + +// Creates a mock engine that responds to platform messages. +static FlEngine* make_mock_engine() { + g_autoptr(FlDartProject) project = fl_dart_project_new("data"); + g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new(); + g_autoptr(FlEngine) engine = fl_engine_new(project, FL_RENDERER(renderer)); + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_engine_start(engine, &error)); + EXPECT_EQ(error, nullptr); + + return static_cast(g_object_ref(engine)); +} + +// Called when the message response is received in the SendMessage test. +static void echo_response_cb(GObject* object, + GAsyncResult* result, + gpointer user_data) { + g_autoptr(GError) error = nullptr; + g_autoptr(FlValue) message = fl_basic_message_channel_send_finish( + FL_BASIC_MESSAGE_CHANNEL(object), result, &error); + EXPECT_NE(message, nullptr); + EXPECT_EQ(error, nullptr); + + EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(message), "Hello World!"); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks sending a message works. +TEST(FlBasicMessageChannelTest, SendMessage) { + 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(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); + g_autoptr(FlBasicMessageChannel) channel = fl_basic_message_channel_new( + messenger, "test/echo", FL_MESSAGE_CODEC(codec)); + g_autoptr(FlValue) message = fl_value_new_string("Hello World!"); + fl_basic_message_channel_send(channel, message, nullptr, echo_response_cb, + loop); + + // Blocks here until echo_response_cb is called. + g_main_loop_run(loop); +} + +// Called when the message response is received in the SendFailure test. +static void failure_response_cb(GObject* object, + GAsyncResult* result, + gpointer user_data) { + g_autoptr(GError) error = nullptr; + g_autoptr(FlValue) message = fl_basic_message_channel_send_finish( + FL_BASIC_MESSAGE_CHANNEL(object), result, &error); + EXPECT_EQ(message, nullptr); + EXPECT_NE(error, nullptr); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks the engine reporting a send failure is handled. +TEST(FlBasicMessageChannelTest, SendFailure) { + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + + g_autoptr(FlEngine) engine = make_mock_engine(); + 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/failure", FL_MESSAGE_CODEC(codec)); + g_autoptr(FlValue) message = fl_value_new_string("Hello World!"); + fl_basic_message_channel_send(channel, message, nullptr, failure_response_cb, + loop); + + // Blocks here until failure_response_cb is called. + g_main_loop_run(loop); +} + +// Called when a message is received from the engine in the ReceiveMessage test. +static void message_cb(FlBasicMessageChannel* channel, + FlValue* message, + FlBasicMessageChannelResponseHandle* response_handle, + gpointer user_data) { + EXPECT_NE(message, nullptr); + EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(message), "Marco!"); + + g_autoptr(GError) error = nullptr; + g_autoptr(FlValue) response = fl_value_new_string("Polo!"); + EXPECT_TRUE(fl_basic_message_channel_respond(channel, response_handle, + response, &error)); + EXPECT_EQ(error, nullptr); +} + +// Called when a the test engine notifies us what response we sent in the +// ReceiveMessage test. +static void response_cb(FlBasicMessageChannel* channel, + FlValue* message, + FlBasicMessageChannelResponseHandle* response_handle, + gpointer user_data) { + EXPECT_NE(message, nullptr); + EXPECT_EQ(fl_value_get_type(message), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(message), "Polo!"); + + fl_basic_message_channel_respond(channel, response_handle, nullptr, nullptr); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks the shell able to receive and respond to messages from the engine. +TEST(FlBasicMessageChannelTest, ReceiveMessage) { + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + + g_autoptr(FlEngine) engine = make_mock_engine(); + FlBinaryMessenger* messenger = fl_binary_messenger_new(engine); + + // Listen for messages from the engine. + g_autoptr(FlStandardMessageCodec) codec = fl_standard_message_codec_new(); + g_autoptr(FlBasicMessageChannel) messages_channel = + fl_basic_message_channel_new(messenger, "test/messages", + FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler(messages_channel, message_cb, + nullptr, nullptr); + + // Listen for response from the engine. + g_autoptr(FlBasicMessageChannel) responses_channel = + fl_basic_message_channel_new(messenger, "test/responses", + FL_MESSAGE_CODEC(codec)); + fl_basic_message_channel_set_message_handler(responses_channel, response_cb, + loop, nullptr); + + // Triggger the engine to send a message. + g_autoptr(FlBasicMessageChannel) control_channel = + fl_basic_message_channel_new(messenger, "test/send-message", + FL_MESSAGE_CODEC(codec)); + g_autoptr(FlValue) message = fl_value_new_string("Marco!"); + fl_basic_message_channel_send(control_channel, message, nullptr, nullptr, + nullptr); + + // Blocks here until response_cb is called. + g_main_loop_run(loop); +} diff --git a/shell/platform/linux/fl_binary_messenger_test.cc b/shell/platform/linux/fl_binary_messenger_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..edbd136586685812591ac7a34d9656a1f997e1f6 --- /dev/null +++ b/shell/platform/linux/fl_binary_messenger_test.cc @@ -0,0 +1,195 @@ +// 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. + +// Included first as it collides with the X11 headers. +#include "gtest/gtest.h" + +#include "flutter/shell/platform/linux/fl_binary_messenger_private.h" +#include "flutter/shell/platform/linux/fl_engine_private.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h" +#include "flutter/shell/platform/linux/testing/mock_renderer.h" + +// Creates a mock engine that responds to platform messages. +static FlEngine* make_mock_engine() { + g_autoptr(FlDartProject) project = fl_dart_project_new("data"); + g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new(); + g_autoptr(FlEngine) engine = fl_engine_new(project, FL_RENDERER(renderer)); + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_engine_start(engine, &error)); + EXPECT_EQ(error, nullptr); + + return static_cast(g_object_ref(engine)); +} + +// Checks sending nullptr for a message works. +TEST(FlBinaryMessengerTest, SendNullptrMessage) { + g_autoptr(FlEngine) engine = make_mock_engine(); + FlBinaryMessenger* messenger = fl_binary_messenger_new(engine); + fl_binary_messenger_send_on_channel(messenger, "test/echo", nullptr, nullptr, + nullptr, nullptr); +} + +// Checks sending a zero length message works. +TEST(FlBinaryMessengerTest, SendEmptyMessage) { + g_autoptr(FlEngine) engine = make_mock_engine(); + FlBinaryMessenger* messenger = fl_binary_messenger_new(engine); + g_autoptr(GBytes) message = g_bytes_new(nullptr, 0); + fl_binary_messenger_send_on_channel(messenger, "test/echo", message, nullptr, + nullptr, nullptr); +} + +// Called when the message response is received in the SendMessage test. +static void echo_response_cb(GObject* object, + GAsyncResult* result, + gpointer user_data) { + g_autoptr(GError) error = nullptr; + g_autoptr(GBytes) message = fl_binary_messenger_send_on_channel_finish( + FL_BINARY_MESSENGER(object), result, &error); + EXPECT_NE(message, nullptr); + EXPECT_EQ(error, nullptr); + + g_autofree gchar* text = + g_strndup(static_cast(g_bytes_get_data(message, nullptr)), + g_bytes_get_size(message)); + EXPECT_STREQ(text, "Hello World!"); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks sending a message works. +TEST(FlBinaryMessengerTest, SendMessage) { + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + + g_autoptr(FlEngine) engine = make_mock_engine(); + FlBinaryMessenger* messenger = fl_binary_messenger_new(engine); + const char* text = "Hello World!"; + g_autoptr(GBytes) message = g_bytes_new(text, strlen(text)); + fl_binary_messenger_send_on_channel(messenger, "test/echo", message, nullptr, + echo_response_cb, loop); + + // Blocks here until echo_response_cb is called. + g_main_loop_run(loop); +} + +// Called when the message response is received in the NullptrResponse test. +static void nullptr_response_cb(GObject* object, + GAsyncResult* result, + gpointer user_data) { + g_autoptr(GError) error = nullptr; + g_autoptr(GBytes) message = fl_binary_messenger_send_on_channel_finish( + FL_BINARY_MESSENGER(object), result, &error); + EXPECT_NE(message, nullptr); + EXPECT_EQ(error, nullptr); + + EXPECT_EQ(g_bytes_get_size(message), static_cast(0)); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks the engine returning a nullptr message work. +TEST(FlBinaryMessengerTest, NullptrResponse) { + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + + g_autoptr(FlEngine) engine = make_mock_engine(); + FlBinaryMessenger* messenger = fl_binary_messenger_new(engine); + const char* text = "Hello World!"; + g_autoptr(GBytes) message = g_bytes_new(text, strlen(text)); + fl_binary_messenger_send_on_channel(messenger, "test/nullptr-response", + message, nullptr, nullptr_response_cb, + loop); + + // Blocks here until nullptr_response_cb is called. + g_main_loop_run(loop); +} + +// Called when the message response is received in the SendFailure test. +static void failure_response_cb(GObject* object, + GAsyncResult* result, + gpointer user_data) { + g_autoptr(GError) error = nullptr; + g_autoptr(GBytes) message = fl_binary_messenger_send_on_channel_finish( + FL_BINARY_MESSENGER(object), result, &error); + EXPECT_EQ(message, nullptr); + EXPECT_NE(error, nullptr); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks the engine reporting a send failure is handled. +TEST(FlBinaryMessengerTest, SendFailure) { + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + + g_autoptr(FlEngine) engine = make_mock_engine(); + FlBinaryMessenger* messenger = fl_binary_messenger_new(engine); + fl_binary_messenger_send_on_channel(messenger, "test/failure", nullptr, + nullptr, failure_response_cb, loop); + + // Blocks here until failure_response_cb is called. + g_main_loop_run(loop); +} + +// Called when a message is received from the engine in the ReceiveMessage test. +static void message_cb(FlBinaryMessenger* messenger, + const gchar* channel, + GBytes* message, + FlBinaryMessengerResponseHandle* response_handle, + gpointer user_data) { + EXPECT_NE(message, nullptr); + g_autofree gchar* text = + g_strndup(static_cast(g_bytes_get_data(message, nullptr)), + g_bytes_get_size(message)); + EXPECT_STREQ(text, "Marco!"); + + const char* response_text = "Polo!"; + g_autoptr(GBytes) response = + g_bytes_new(response_text, strlen(response_text)); + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_binary_messenger_send_response(messenger, response_handle, + response, &error)); + EXPECT_EQ(error, nullptr); +} + +// Called when a the test engine notifies us what response we sent in the +// ReceiveMessage test. +static void response_cb(FlBinaryMessenger* messenger, + const gchar* channel, + GBytes* message, + FlBinaryMessengerResponseHandle* response_handle, + gpointer user_data) { + EXPECT_NE(message, nullptr); + g_autofree gchar* text = + g_strndup(static_cast(g_bytes_get_data(message, nullptr)), + g_bytes_get_size(message)); + EXPECT_STREQ(text, "Polo!"); + + fl_binary_messenger_send_response(messenger, response_handle, nullptr, + nullptr); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks the shell able to receive and respond to messages from the engine. +TEST(FlBinaryMessengerTest, ReceiveMessage) { + g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0); + + g_autoptr(FlEngine) engine = make_mock_engine(); + FlBinaryMessenger* messenger = fl_binary_messenger_new(engine); + + // Listen for messages from the engine. + fl_binary_messenger_set_message_handler_on_channel( + messenger, "test/messages", message_cb, nullptr, nullptr); + + // Listen for response from the engine. + fl_binary_messenger_set_message_handler_on_channel( + messenger, "test/responses", response_cb, loop, nullptr); + + // Triggger the engine to send a message. + const char* text = "Marco!"; + g_autoptr(GBytes) message = g_bytes_new(text, strlen(text)); + fl_binary_messenger_send_on_channel(messenger, "test/send-message", message, + nullptr, nullptr, nullptr); + + // Blocks here until response_cb is called. + g_main_loop_run(loop); +} diff --git a/shell/platform/linux/fl_method_channel_test.cc b/shell/platform/linux/fl_method_channel_test.cc new file mode 100644 index 0000000000000000000000000000000000000000..2a5ab6af78765c00c91bcb08feb02ca6bbf4bfc6 --- /dev/null +++ b/shell/platform/linux/fl_method_channel_test.cc @@ -0,0 +1,434 @@ +// 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. + +// Included first as it collides with the X11 headers. +#include "gtest/gtest.h" + +#include "flutter/shell/platform/linux/fl_binary_messenger_private.h" +#include "flutter/shell/platform/linux/fl_engine_private.h" +#include "flutter/shell/platform/linux/fl_method_codec_private.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_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/testing/mock_renderer.h" + +// Creates a mock engine that responds to platform messages. +static FlEngine* make_mock_engine() { + g_autoptr(FlDartProject) project = fl_dart_project_new("data"); + g_autoptr(FlMockRenderer) renderer = fl_mock_renderer_new(); + g_autoptr(FlEngine) engine = fl_engine_new(project, FL_RENDERER(renderer)); + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_engine_start(engine, &error)); + EXPECT_EQ(error, nullptr); + + return static_cast(g_object_ref(engine)); +} + +// Called when when the method call response is received in the InvokeMethod +// test. +static void method_response_cb(GObject* object, + GAsyncResult* result, + gpointer user_data) { + g_autoptr(GError) error = nullptr; + g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish( + FL_METHOD_CHANNEL(object), result, &error); + EXPECT_NE(response, nullptr); + EXPECT_EQ(error, nullptr); + + FlValue* r = fl_method_response_get_result(response, &error); + EXPECT_NE(r, nullptr); + EXPECT_EQ(error, nullptr); + + EXPECT_EQ(fl_value_get_type(r), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(r), "Hello World!"); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks if invoking a method returns a value. +TEST(FlMethodChannelTest, InvokeMethod) { + 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(FlMethodChannel) channel = fl_method_channel_new( + messenger, "test/standard-method", FL_METHOD_CODEC(codec)); + + g_autoptr(FlValue) args = fl_value_new_string("Hello World!"); + fl_method_channel_invoke_method(channel, "Echo", args, nullptr, + method_response_cb, loop); + + // Blocks here until method_response_cb is called. + g_main_loop_run(loop); +} + +// Called when when the method call response is received in the +// InvokeMethodNullptrArgsMessage test. +static void nullptr_args_response_cb(GObject* object, + GAsyncResult* result, + gpointer user_data) { + g_autoptr(GError) error = nullptr; + g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish( + FL_METHOD_CHANNEL(object), result, &error); + EXPECT_NE(response, nullptr); + EXPECT_EQ(error, nullptr); + + FlValue* r = fl_method_response_get_result(response, &error); + EXPECT_NE(r, nullptr); + EXPECT_EQ(error, nullptr); + EXPECT_EQ(fl_value_get_type(r), FL_VALUE_TYPE_NULL); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks if a method can be invoked with nullptr for arguments. +TEST(FlMethodChannelTest, InvokeMethodNullptrArgsMessage) { + 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(FlMethodChannel) channel = fl_method_channel_new( + messenger, "test/standard-method", FL_METHOD_CODEC(codec)); + + fl_method_channel_invoke_method(channel, "Echo", nullptr, nullptr, + nullptr_args_response_cb, loop); + + // Blocks here until nullptr_args_response_cb is called. + g_main_loop_run(loop); +} + +// Called when when the method call response is received in the +// InvokeMethodError test. +static void error_response_cb(GObject* object, + GAsyncResult* result, + gpointer user_data) { + g_autoptr(GError) error = nullptr; + g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish( + FL_METHOD_CHANNEL(object), result, &error); + EXPECT_NE(response, nullptr); + EXPECT_EQ(error, nullptr); + + EXPECT_TRUE(FL_IS_METHOD_ERROR_RESPONSE(response)); + EXPECT_STREQ( + fl_method_error_response_get_code(FL_METHOD_ERROR_RESPONSE(response)), + "CODE"); + EXPECT_STREQ( + fl_method_error_response_get_message(FL_METHOD_ERROR_RESPONSE(response)), + "MESSAGE"); + FlValue* details = + fl_method_error_response_get_details(FL_METHOD_ERROR_RESPONSE(response)); + EXPECT_NE(details, nullptr); + EXPECT_EQ(fl_value_get_type(details), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(details), "DETAILS"); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks if an error response from a method call is handled. +TEST(FlMethodChannelTest, InvokeMethodError) { + 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(FlMethodChannel) channel = fl_method_channel_new( + messenger, "test/standard-method", FL_METHOD_CODEC(codec)); + + g_autoptr(FlValue) args = fl_value_new_list(); + fl_value_append_take(args, fl_value_new_string("CODE")); + fl_value_append_take(args, fl_value_new_string("MESSAGE")); + fl_value_append_take(args, fl_value_new_string("DETAILS")); + fl_method_channel_invoke_method(channel, "Error", args, nullptr, + error_response_cb, loop); + + // Blocks here until error_response_cb is called. + g_main_loop_run(loop); +} + +// Called when when the method call response is received in the +// InvokeMethodNotImplemented test. +static void not_implemented_response_cb(GObject* object, + GAsyncResult* result, + gpointer user_data) { + g_autoptr(GError) error = nullptr; + g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish( + FL_METHOD_CHANNEL(object), result, &error); + EXPECT_NE(response, nullptr); + EXPECT_EQ(error, nullptr); + + EXPECT_TRUE(FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response)); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks if a not implemeneted response from a method call is handled. +TEST(FlMethodChannelTest, InvokeMethodNotImplemented) { + 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(FlMethodChannel) channel = fl_method_channel_new( + messenger, "test/standard-method", FL_METHOD_CODEC(codec)); + + fl_method_channel_invoke_method(channel, "NotImplemented", nullptr, nullptr, + not_implemented_response_cb, loop); + + // Blocks here until not_implemented_response_cb is called. + g_main_loop_run(loop); +} + +// Called when when the method call response is received in the +// InvokeMethodFailure test. +static void failure_response_cb(GObject* object, + GAsyncResult* result, + gpointer user_data) { + g_autoptr(GError) error = nullptr; + g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish( + FL_METHOD_CHANNEL(object), result, &error); + EXPECT_EQ(response, nullptr); + EXPECT_NE(error, nullptr); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks if an engine failure calling a method call is handled. +TEST(FlMethodChannelTest, InvokeMethodFailure) { + 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(FlMethodChannel) channel = + fl_method_channel_new(messenger, "test/failure", FL_METHOD_CODEC(codec)); + + fl_method_channel_invoke_method(channel, "Echo", nullptr, nullptr, + failure_response_cb, loop); + + // Blocks here until failure_response_cb is called. + g_main_loop_run(loop); +} + +// Called when a method call is received from the engine in the +// ReceiveMethodCallRespondSuccess test. +static void method_call_success_cb(FlMethodChannel* channel, + FlMethodCall* method_call, + gpointer user_data) { + EXPECT_STREQ(fl_method_call_get_name(method_call), "Foo"); + EXPECT_EQ(fl_value_get_type(fl_method_call_get_args(method_call)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_method_call_get_args(method_call)), + "Marco!"); + + 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); +} + +// Called when a the test engine notifies us what response we sent in the +// ReceiveMethodCallRespondSuccess test. +static void method_call_success_response_cb( + FlBinaryMessenger* messenger, + const gchar* channel, + GBytes* message, + FlBinaryMessengerResponseHandle* response_handle, + gpointer user_data) { + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + g_autoptr(GError) error = nullptr; + g_autoptr(FlMethodResponse) response = + fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error); + EXPECT_NE(response, nullptr); + EXPECT_EQ(error, nullptr); + + EXPECT_TRUE(FL_IS_METHOD_SUCCESS_RESPONSE(response)); + FlValue* result = fl_method_success_response_get_result( + FL_METHOD_SUCCESS_RESPONSE(response)); + EXPECT_EQ(fl_value_get_type(result), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(result), "Polo!"); + + fl_binary_messenger_send_response(messenger, response_handle, nullptr, + nullptr); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks the shell able to receive and respond to method calls from the engine. +TEST(FlMethodChannelTest, ReceiveMethodCallRespondSuccess) { + 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(FlMethodChannel) channel = fl_method_channel_new( + messenger, "test/standard-method", FL_METHOD_CODEC(codec)); + fl_method_channel_set_method_call_handler(channel, method_call_success_cb, + nullptr, nullptr); + + // Listen for response from the engine. + fl_binary_messenger_set_message_handler_on_channel( + messenger, "test/responses", method_call_success_response_cb, loop, + nullptr); + + // Trigger the engine to make a method call. + g_autoptr(FlValue) args = fl_value_new_list(); + fl_value_append_take(args, fl_value_new_string("test/standard-method")); + fl_value_append_take(args, fl_value_new_string("Foo")); + fl_value_append_take(args, fl_value_new_string("Marco!")); + fl_method_channel_invoke_method(channel, "InvokeMethod", args, nullptr, + nullptr, loop); + + // Blocks here until method_call_success_response_cb is called. + g_main_loop_run(loop); +} + +// Called when a method call is received from the engine in the +// ReceiveMethodCallRespondError test. +static void method_call_error_cb(FlMethodChannel* channel, + FlMethodCall* method_call, + gpointer user_data) { + EXPECT_STREQ(fl_method_call_get_name(method_call), "Foo"); + EXPECT_EQ(fl_value_get_type(fl_method_call_get_args(method_call)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_method_call_get_args(method_call)), + "Marco!"); + + g_autoptr(FlValue) details = fl_value_new_string("DETAILS"); + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_method_call_respond_error(method_call, "CODE", "MESSAGE", + details, &error)); + EXPECT_EQ(error, nullptr); +} + +// Called when a the test engine notifies us what response we sent in the +// ReceiveMethodCallRespondError test. +static void method_call_error_response_cb( + FlBinaryMessenger* messenger, + const gchar* channel, + GBytes* message, + FlBinaryMessengerResponseHandle* response_handle, + gpointer user_data) { + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + g_autoptr(GError) error = nullptr; + g_autoptr(FlMethodResponse) response = + fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error); + EXPECT_NE(response, nullptr); + EXPECT_EQ(error, nullptr); + + EXPECT_TRUE(FL_IS_METHOD_ERROR_RESPONSE(response)); + EXPECT_STREQ( + fl_method_error_response_get_code(FL_METHOD_ERROR_RESPONSE(response)), + "CODE"); + EXPECT_STREQ( + fl_method_error_response_get_message(FL_METHOD_ERROR_RESPONSE(response)), + "MESSAGE"); + FlValue* details = + fl_method_error_response_get_details(FL_METHOD_ERROR_RESPONSE(response)); + EXPECT_EQ(fl_value_get_type(details), FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(details), "DETAILS"); + + fl_binary_messenger_send_response(messenger, response_handle, nullptr, + nullptr); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks the shell able to receive and respond to method calls from the engine. +TEST(FlMethodChannelTest, ReceiveMethodCallRespondError) { + 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(FlMethodChannel) channel = fl_method_channel_new( + messenger, "test/standard-method", FL_METHOD_CODEC(codec)); + fl_method_channel_set_method_call_handler(channel, method_call_error_cb, + nullptr, nullptr); + + // Listen for response from the engine. + fl_binary_messenger_set_message_handler_on_channel( + messenger, "test/responses", method_call_error_response_cb, loop, + nullptr); + + // Trigger the engine to make a method call. + g_autoptr(FlValue) args = fl_value_new_list(); + fl_value_append_take(args, fl_value_new_string("test/standard-method")); + fl_value_append_take(args, fl_value_new_string("Foo")); + fl_value_append_take(args, fl_value_new_string("Marco!")); + fl_method_channel_invoke_method(channel, "InvokeMethod", args, nullptr, + nullptr, loop); + + // Blocks here until method_call_error_response_cb is called. + g_main_loop_run(loop); +} + +// Called when a method call is received from the engine in the +// ReceiveMethodCallRespondNotImplemented test. +static void method_call_not_implemented_cb(FlMethodChannel* channel, + FlMethodCall* method_call, + gpointer user_data) { + EXPECT_STREQ(fl_method_call_get_name(method_call), "Foo"); + EXPECT_EQ(fl_value_get_type(fl_method_call_get_args(method_call)), + FL_VALUE_TYPE_STRING); + EXPECT_STREQ(fl_value_get_string(fl_method_call_get_args(method_call)), + "Marco!"); + + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_method_call_respond_not_implemented(method_call, &error)); + EXPECT_EQ(error, nullptr); +} + +// Called when a the test engine notifies us what response we sent in the +// ReceiveMethodCallRespondNotImplemented test. +static void method_call_not_implemented_response_cb( + FlBinaryMessenger* messenger, + const gchar* channel, + GBytes* message, + FlBinaryMessengerResponseHandle* response_handle, + gpointer user_data) { + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + g_autoptr(GError) error = nullptr; + g_autoptr(FlMethodResponse) response = + fl_method_codec_decode_response(FL_METHOD_CODEC(codec), message, &error); + EXPECT_NE(response, nullptr); + EXPECT_EQ(error, nullptr); + + EXPECT_TRUE(FL_IS_METHOD_NOT_IMPLEMENTED_RESPONSE(response)); + + fl_binary_messenger_send_response(messenger, response_handle, nullptr, + nullptr); + + g_main_loop_quit(static_cast(user_data)); +} + +// Checks the shell able to receive and respond to method calls from the engine. +TEST(FlMethodChannelTest, ReceiveMethodCallRespondNotImplemented) { + 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(FlMethodChannel) channel = fl_method_channel_new( + messenger, "test/standard-method", FL_METHOD_CODEC(codec)); + fl_method_channel_set_method_call_handler( + channel, method_call_not_implemented_cb, nullptr, nullptr); + + // Listen for response from the engine. + fl_binary_messenger_set_message_handler_on_channel( + messenger, "test/responses", method_call_not_implemented_response_cb, + loop, nullptr); + + // Trigger the engine to make a method call. + g_autoptr(FlValue) args = fl_value_new_list(); + fl_value_append_take(args, fl_value_new_string("test/standard-method")); + fl_value_append_take(args, fl_value_new_string("Foo")); + fl_value_append_take(args, fl_value_new_string("Marco!")); + fl_method_channel_invoke_method(channel, "InvokeMethod", args, nullptr, + nullptr, loop); + + // Blocks here until method_call_not_implemented_response_cb is called. + g_main_loop_run(loop); +} diff --git a/shell/platform/linux/testing/mock_egl.cc b/shell/platform/linux/testing/mock_egl.cc new file mode 100644 index 0000000000000000000000000000000000000000..53edb697b6f2f0bc91ddb21601203087b0c461ad --- /dev/null +++ b/shell/platform/linux/testing/mock_egl.cc @@ -0,0 +1,65 @@ +// 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 + +EGLBoolean eglBindAPI(EGLenum api) { + return EGL_TRUE; +} + +EGLBoolean eglChooseConfig(EGLDisplay dpy, + const EGLint* attrib_list, + EGLConfig* configs, + EGLint config_size, + EGLint* num_config) { + return EGL_TRUE; +} + +EGLContext eglCreateContext(EGLDisplay dpy, + EGLConfig config, + EGLContext share_context, + const EGLint* attrib_list) { + return nullptr; +} + +EGLSurface eglCreateWindowSurface(EGLDisplay dpy, + EGLConfig config, + EGLNativeWindowType win, + const EGLint* attrib_list) { + return nullptr; +} + +EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id) { + return nullptr; +} + +void (*eglGetProcAddress(const char* procname))(void) { + return nullptr; +} + +EGLBoolean eglInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor) { + if (major != nullptr) + *major = 1; + if (minor != nullptr) + *major = 5; + return EGL_TRUE; +} + +EGLBoolean eglMakeCurrent(EGLDisplay dpy, + EGLSurface draw, + EGLSurface read, + EGLContext ctx) { + return EGL_TRUE; +} + +EGLBoolean eglQueryContext(EGLDisplay dpy, + EGLContext ctx, + EGLint attribute, + EGLint* value) { + return EGL_TRUE; +} + +EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface surface) { + return EGL_TRUE; +} diff --git a/shell/platform/linux/testing/mock_engine.cc b/shell/platform/linux/testing/mock_engine.cc new file mode 100644 index 0000000000000000000000000000000000000000..3961cb053a292db83993b25b4695db4688e71551 --- /dev/null +++ b/shell/platform/linux/testing/mock_engine.cc @@ -0,0 +1,369 @@ +// 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/embedder/embedder.h" +#include "flutter/shell/platform/linux/fl_method_codec_private.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_response.h" +#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h" +#include "gtest/gtest.h" + +#include + +struct _FlutterEngine { + bool running; + FlutterPlatformMessageCallback platform_message_callback; + FlutterTaskRunnerPostTaskCallback platform_post_task_callback; + void* user_data; + + _FlutterEngine(FlutterPlatformMessageCallback platform_message_callback, + FlutterTaskRunnerPostTaskCallback platform_post_task_callback, + void* user_data) + : running(false), + platform_message_callback(platform_message_callback), + platform_post_task_callback(platform_post_task_callback), + user_data(user_data) {} +}; + +struct _FlutterPlatformMessageResponseHandle { + FlutterDataCallback data_callback; + void* user_data; + std::string channel; + bool released; + + // Constructor for a response handle generated by the engine. + _FlutterPlatformMessageResponseHandle(std::string channel) + : data_callback(nullptr), + user_data(nullptr), + channel(channel), + released(false) {} + + // Constructor for a response handle generated by the shell. + _FlutterPlatformMessageResponseHandle(FlutterDataCallback data_callback, + void* user_data) + : data_callback(data_callback), user_data(user_data), released(false) {} +}; + +struct _FlutterTaskRunner { + uint64_t task; + std::string channel; + const FlutterPlatformMessageResponseHandle* response_handle; + uint8_t* message; + size_t message_size; + + _FlutterTaskRunner( + uint64_t task, + const std::string& channel, + const FlutterPlatformMessageResponseHandle* response_handle, + const uint8_t* message, + size_t message_size) + : task(task), + channel(channel), + response_handle(response_handle), + message_size(message_size) { + this->message = static_cast(malloc(message_size)); + memcpy(this->message, message, message_size); + } + ~_FlutterTaskRunner() { + if (response_handle != nullptr) { + EXPECT_TRUE(response_handle->released); + delete response_handle; + } + free(message); + } +}; + +// Send a response from the engine. +static void send_response( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const std::string& channel, + const FlutterPlatformMessageResponseHandle* response_handle, + const uint8_t* message, + size_t message_size) { + if (response_handle == nullptr) + return; + + FlutterTask task; + task.runner = new _FlutterTaskRunner(1234, channel, response_handle, message, + message_size); + task.task = task.runner->task; + engine->platform_post_task_callback(task, 0, engine->user_data); +} + +// Send a message from the engine. +static void send_message(FLUTTER_API_SYMBOL(FlutterEngine) engine, + const std::string& channel, + const uint8_t* message, + size_t message_size) { + FlutterTask task; + task.runner = + new _FlutterTaskRunner(1234, channel, nullptr, message, message_size); + task.task = task.runner->task; + engine->platform_post_task_callback(task, 0, engine->user_data); +} + +static void invoke_method(FLUTTER_API_SYMBOL(FlutterEngine) engine, + const std::string& channel, + const gchar* name, + FlValue* args) { + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + g_autoptr(GError) error = nullptr; + g_autoptr(GBytes) message = fl_method_codec_encode_method_call( + FL_METHOD_CODEC(codec), name, args, &error); + EXPECT_NE(message, nullptr); + EXPECT_EQ(error, nullptr); + + FlutterTask task; + task.runner = new _FlutterTaskRunner( + 1234, channel, nullptr, + static_cast(g_bytes_get_data(message, nullptr)), + g_bytes_get_size(message)); + task.task = task.runner->task; + engine->platform_post_task_callback(task, 0, engine->user_data); +} + +FlutterEngineResult FlutterEngineRun(size_t version, + const FlutterRendererConfig* config, + const FlutterProjectArgs* args, + void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) * + engine_out) { + EXPECT_NE(config, nullptr); + EXPECT_NE(args, nullptr); + EXPECT_NE(user_data, nullptr); + EXPECT_NE(engine_out, nullptr); + + FlutterEngineResult result = + FlutterEngineInitialize(version, config, args, user_data, engine_out); + if (result != kSuccess) + return result; + return FlutterEngineRunInitialized(*engine_out); +} + +FlutterEngineResult FlutterEngineShutdown(FLUTTER_API_SYMBOL(FlutterEngine) + engine) { + delete engine; + return kSuccess; +} + +FlutterEngineResult FlutterEngineInitialize(size_t version, + const FlutterRendererConfig* config, + const FlutterProjectArgs* args, + void* user_data, + FLUTTER_API_SYMBOL(FlutterEngine) * + engine_out) { + EXPECT_NE(config, nullptr); + + EXPECT_NE(args, nullptr); + EXPECT_NE(args->platform_message_callback, nullptr); + EXPECT_NE(args->custom_task_runners, nullptr); + EXPECT_NE(args->custom_task_runners->platform_task_runner, nullptr); + EXPECT_NE(args->custom_task_runners->platform_task_runner->post_task_callback, + nullptr); + + EXPECT_NE(user_data, nullptr); + + EXPECT_EQ(config->type, kOpenGL); + + *engine_out = new _FlutterEngine( + args->platform_message_callback, + args->custom_task_runners->platform_task_runner->post_task_callback, + user_data); + return kSuccess; +} + +FlutterEngineResult FlutterEngineDeinitialize(FLUTTER_API_SYMBOL(FlutterEngine) + engine) { + return kSuccess; +} + +FlutterEngineResult FlutterEngineRunInitialized( + FLUTTER_API_SYMBOL(FlutterEngine) engine) { + engine->running = true; + return kSuccess; +} + +FlutterEngineResult FlutterEngineSendWindowMetricsEvent( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterWindowMetricsEvent* event) { + EXPECT_TRUE(engine->running); + return kSuccess; +} + +FlutterEngineResult FlutterEngineSendPointerEvent( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterPointerEvent* events, + size_t events_count) { + return kSuccess; +} + +FLUTTER_EXPORT +FlutterEngineResult FlutterEngineSendPlatformMessage( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterPlatformMessage* message) { + EXPECT_TRUE(engine->running); + + if (strcmp(message->channel, "test/echo") == 0) { + // Responds with the same message received. + send_response(engine, message->channel, message->response_handle, + message->message, message->message_size); + } else if (strcmp(message->channel, "test/send-message") == 0) { + // Triggers the engine to send a message. + send_response(engine, message->channel, message->response_handle, nullptr, + 0); + send_message(engine, "test/messages", message->message, + message->message_size); + } else if (strcmp(message->channel, "test/standard-method") == 0) { + g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); + g_autoptr(GBytes) m = g_bytes_new(message->message, message->message_size); + g_autofree gchar* name = nullptr; + g_autoptr(FlValue) args = nullptr; + g_autoptr(GError) error = nullptr; + EXPECT_TRUE(fl_method_codec_decode_method_call(FL_METHOD_CODEC(codec), m, + &name, &args, &error)); + EXPECT_EQ(error, nullptr); + + g_autoptr(GBytes) response = nullptr; + if (strcmp(name, "Echo") == 0) { + // Returns args as a success result. + response = fl_method_codec_encode_success_envelope(FL_METHOD_CODEC(codec), + args, &error); + EXPECT_EQ(error, nullptr); + } else if (strcmp(name, "Error") == 0) { + // Returns an error result. + const gchar* code = nullptr; + const gchar* message = nullptr; + FlValue* details = nullptr; + if (fl_value_get_length(args) >= 2) { + FlValue* code_value = fl_value_get_list_value(args, 0); + EXPECT_EQ(fl_value_get_type(code_value), FL_VALUE_TYPE_STRING); + code = fl_value_get_string(code_value); + FlValue* message_value = fl_value_get_list_value(args, 1); + message = fl_value_get_type(message_value) == FL_VALUE_TYPE_STRING + ? fl_value_get_string(message_value) + : nullptr; + } + if (fl_value_get_length(args) >= 3) + details = fl_value_get_list_value(args, 2); + response = fl_method_codec_encode_error_envelope( + FL_METHOD_CODEC(codec), code, message, details, &error); + EXPECT_EQ(error, nullptr); + } else if (strcmp(name, "InvokeMethod") == 0) { + // Gets the engine to call the shell. + if (fl_value_get_length(args) == 3) { + FlValue* channel_value = fl_value_get_list_value(args, 0); + EXPECT_EQ(fl_value_get_type(channel_value), FL_VALUE_TYPE_STRING); + const gchar* channel = fl_value_get_string(channel_value); + FlValue* name_value = fl_value_get_list_value(args, 1); + EXPECT_EQ(fl_value_get_type(name_value), FL_VALUE_TYPE_STRING); + const gchar* name = fl_value_get_string(name_value); + FlValue* method_args = fl_value_get_list_value(args, 2); + invoke_method(engine, channel, name, method_args); + } + response = fl_method_codec_encode_success_envelope(FL_METHOD_CODEC(codec), + nullptr, &error); + EXPECT_EQ(error, nullptr); + } else { + // Returns "not implemented". + response = g_bytes_new(nullptr, 0); + } + + send_response( + engine, message->channel, message->response_handle, + static_cast(g_bytes_get_data(response, nullptr)), + g_bytes_get_size(response)); + } else if (strcmp(message->channel, "test/nullptr-response") == 0) { + // Sends a null response. + send_response(engine, message->channel, message->response_handle, nullptr, + 0); + } else if (strcmp(message->channel, "test/failure") == 0) { + // Generates an internal error. + return kInternalInconsistency; + } + + return kSuccess; +} + +FlutterEngineResult FlutterPlatformMessageCreateResponseHandle( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + FlutterDataCallback data_callback, + void* user_data, + FlutterPlatformMessageResponseHandle** response_out) { + EXPECT_TRUE(engine->running); + EXPECT_NE(data_callback, nullptr); + EXPECT_NE(user_data, nullptr); + + _FlutterPlatformMessageResponseHandle* handle = + new _FlutterPlatformMessageResponseHandle(data_callback, user_data); + + *response_out = handle; + return kSuccess; +} + +FlutterEngineResult FlutterPlatformMessageReleaseResponseHandle( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + FlutterPlatformMessageResponseHandle* response) { + EXPECT_NE(engine, nullptr); + EXPECT_NE(response, nullptr); + + EXPECT_TRUE(engine->running); + + EXPECT_FALSE(response->released); + response->released = true; + + return kSuccess; +} + +FlutterEngineResult FlutterEngineSendPlatformMessageResponse( + FLUTTER_API_SYMBOL(FlutterEngine) engine, + const FlutterPlatformMessageResponseHandle* handle, + const uint8_t* data, + size_t data_length) { + EXPECT_NE(engine, nullptr); + EXPECT_NE(handle, nullptr); + + EXPECT_TRUE(engine->running); + + // Send a message so the shell can check the responses received. + if (handle->channel != "test/responses") + send_message(engine, "test/responses", data, data_length); + + EXPECT_FALSE(handle->released); + + delete handle; + + return kSuccess; +} + +FlutterEngineResult FlutterEngineRunTask(FLUTTER_API_SYMBOL(FlutterEngine) + engine, + const FlutterTask* task) { + EXPECT_NE(engine, nullptr); + EXPECT_NE(task, nullptr); + EXPECT_NE(task->runner, nullptr); + + FlutterTaskRunner runner = task->runner; + EXPECT_NE(runner, nullptr); + const FlutterPlatformMessageResponseHandle* response_handle = + runner->response_handle; + if (response_handle != nullptr) { + EXPECT_NE(response_handle->data_callback, nullptr); + response_handle->data_callback(runner->message, runner->message_size, + response_handle->user_data); + } else { + _FlutterPlatformMessageResponseHandle* handle = + new _FlutterPlatformMessageResponseHandle(runner->channel); + + FlutterPlatformMessage message; + message.struct_size = sizeof(FlutterPlatformMessage); + message.channel = runner->channel.c_str(); + message.message = runner->message; + message.message_size = runner->message_size; + message.response_handle = handle; + engine->platform_message_callback(&message, engine->user_data); + } + + delete runner; + + return kSuccess; +} diff --git a/shell/platform/linux/testing/mock_renderer.cc b/shell/platform/linux/testing/mock_renderer.cc new file mode 100644 index 0000000000000000000000000000000000000000..03ab5482d9e723b5c91121987c8e056e5df4fdf4 --- /dev/null +++ b/shell/platform/linux/testing/mock_renderer.cc @@ -0,0 +1,27 @@ +// 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_renderer.h" + +struct _FlMockRenderer { + FlRenderer parent_instance; +}; + +G_DEFINE_TYPE(FlMockRenderer, fl_mock_renderer, fl_renderer_get_type()) + +// Implements FlRenderer::start +static gboolean fl_mock_renderer_start(FlRenderer* renderer, GError** error) { + return TRUE; +} + +static void fl_mock_renderer_class_init(FlMockRendererClass* klass) { + FL_RENDERER_CLASS(klass)->start = fl_mock_renderer_start; +} + +static void fl_mock_renderer_init(FlMockRenderer* self) {} + +// Creates a stub renderer +FlMockRenderer* fl_mock_renderer_new() { + return FL_MOCK_RENDERER(g_object_new(fl_mock_renderer_get_type(), nullptr)); +} diff --git a/shell/platform/linux/testing/mock_renderer.h b/shell/platform/linux/testing/mock_renderer.h new file mode 100644 index 0000000000000000000000000000000000000000..19268683cf18719ac277bc01dbd141981fdf658b --- /dev/null +++ b/shell/platform/linux/testing/mock_renderer.h @@ -0,0 +1,17 @@ +// 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/fl_renderer.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(FlMockRenderer, + fl_mock_renderer, + FL, + MOCK_RENDERER, + FlRenderer) + +FlMockRenderer* fl_mock_renderer_new(); + +G_END_DECLS