Flutter Engine
The Flutter Engine
fl_engine_test.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Included first as it collides with the X11 headers.
6#include "gtest/gtest.h"
7
8#include "flutter/shell/platform/common/app_lifecycle_state.h"
9#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
10#include "flutter/shell/platform/linux/fl_engine_private.h"
11#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
12#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
13#include "flutter/shell/platform/linux/public/flutter_linux/fl_string_codec.h"
14#include "flutter/shell/platform/linux/testing/fl_test.h"
15
16// MOCK_ENGINE_PROC is leaky by design
17// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
18
19// Checks sending window metrics events works.
20TEST(FlEngineTest, WindowMetrics) {
21 g_autoptr(FlEngine) engine = make_mock_engine();
23
24 bool called = false;
26 SendWindowMetricsEvent,
27 ([&called](auto engine, const FlutterWindowMetricsEvent* event) {
28 called = true;
29 EXPECT_EQ(event->width, static_cast<size_t>(3840));
30 EXPECT_EQ(event->height, static_cast<size_t>(2160));
31 EXPECT_EQ(event->pixel_ratio, 2.0);
32
33 return kSuccess;
34 }));
35
36 g_autoptr(GError) error = nullptr;
38 EXPECT_EQ(error, nullptr);
40
41 EXPECT_TRUE(called);
42}
43
44// Checks sending mouse pointer events works.
45TEST(FlEngineTest, MousePointer) {
46 g_autoptr(FlEngine) engine = make_mock_engine();
48
49 bool called = false;
50 embedder_api->SendPointerEvent = MOCK_ENGINE_PROC(
51 SendPointerEvent,
52 ([&called](auto engine, const FlutterPointerEvent* events,
53 size_t events_count) {
54 called = true;
55 EXPECT_EQ(events_count, static_cast<size_t>(1));
56 EXPECT_EQ(events[0].phase, kDown);
57 EXPECT_EQ(events[0].timestamp, static_cast<size_t>(1234567890));
58 EXPECT_EQ(events[0].x, 800);
59 EXPECT_EQ(events[0].y, 600);
60 EXPECT_EQ(events[0].device, static_cast<int32_t>(0));
61 EXPECT_EQ(events[0].signal_kind, kFlutterPointerSignalKindScroll);
62 EXPECT_EQ(events[0].scroll_delta_x, 1.2);
63 EXPECT_EQ(events[0].scroll_delta_y, -3.4);
64 EXPECT_EQ(events[0].device_kind, kFlutterPointerDeviceKindMouse);
65 EXPECT_EQ(events[0].buttons, kFlutterPointerButtonMouseSecondary);
66
67 return kSuccess;
68 }));
69
70 g_autoptr(GError) error = nullptr;
72 EXPECT_EQ(error, nullptr);
73 fl_engine_send_mouse_pointer_event(engine, kDown, 1234567890, 800, 600,
76
77 EXPECT_TRUE(called);
78}
79
80// Checks sending pan/zoom events works.
81TEST(FlEngineTest, PointerPanZoom) {
82 g_autoptr(FlEngine) engine = make_mock_engine();
84
85 bool called = false;
86 embedder_api->SendPointerEvent = MOCK_ENGINE_PROC(
87 SendPointerEvent,
88 ([&called](auto engine, const FlutterPointerEvent* events,
89 size_t events_count) {
90 called = true;
91 EXPECT_EQ(events_count, static_cast<size_t>(1));
92 EXPECT_EQ(events[0].phase, kPanZoomUpdate);
93 EXPECT_EQ(events[0].timestamp, static_cast<size_t>(1234567890));
94 EXPECT_EQ(events[0].x, 800);
95 EXPECT_EQ(events[0].y, 600);
96 EXPECT_EQ(events[0].device, static_cast<int32_t>(1));
97 EXPECT_EQ(events[0].signal_kind, kFlutterPointerSignalKindNone);
98 EXPECT_EQ(events[0].pan_x, 1.5);
99 EXPECT_EQ(events[0].pan_y, 2.5);
100 EXPECT_EQ(events[0].scale, 3.5);
101 EXPECT_EQ(events[0].rotation, 4.5);
102 EXPECT_EQ(events[0].device_kind, kFlutterPointerDeviceKindTrackpad);
103 EXPECT_EQ(events[0].buttons, 0);
104
105 return kSuccess;
106 }));
107
108 g_autoptr(GError) error = nullptr;
110 EXPECT_EQ(error, nullptr);
111 fl_engine_send_pointer_pan_zoom_event(engine, 1234567890, 800, 600,
112 kPanZoomUpdate, 1.5, 2.5, 3.5, 4.5);
113
114 EXPECT_TRUE(called);
115}
116
117// Checks dispatching a semantics action works.
119 g_autoptr(FlEngine) engine = make_mock_engine();
121
122 bool called = false;
125 ([&called](auto engine, uint64_t id, FlutterSemanticsAction action,
126 const uint8_t* data, size_t data_length) {
127 EXPECT_EQ(id, static_cast<uint64_t>(42));
129 EXPECT_EQ(data_length, static_cast<size_t>(4));
130 EXPECT_EQ(data[0], 't');
131 EXPECT_EQ(data[1], 'e');
132 EXPECT_EQ(data[2], 's');
133 EXPECT_EQ(data[3], 't');
134 called = true;
135
136 return kSuccess;
137 }));
138
139 g_autoptr(GError) error = nullptr;
141 EXPECT_EQ(error, nullptr);
142 g_autoptr(GBytes) data = g_bytes_new_static("test", 4);
144 data);
145
146 EXPECT_TRUE(called);
147}
148
149// Checks sending platform messages works.
150TEST(FlEngineTest, PlatformMessage) {
151 g_autoptr(FlEngine) engine = make_mock_engine();
153
154 bool called = false;
156 embedder_api->SendPlatformMessage;
158 SendPlatformMessage,
159 ([&called, old_handler](auto engine,
161 if (strcmp(message->channel, "test") != 0) {
162 return old_handler(engine, message);
163 }
164
165 called = true;
166
167 EXPECT_EQ(message->message_size, static_cast<size_t>(4));
168 EXPECT_EQ(message->message[0], 't');
169 EXPECT_EQ(message->message[1], 'e');
170 EXPECT_EQ(message->message[2], 's');
171 EXPECT_EQ(message->message[3], 't');
172
173 return kSuccess;
174 }));
175
176 g_autoptr(GError) error = nullptr;
178 EXPECT_EQ(error, nullptr);
179 g_autoptr(GBytes) message = g_bytes_new_static("test", 4);
180 fl_engine_send_platform_message(engine, "test", message, nullptr, nullptr,
181 nullptr);
182
183 EXPECT_TRUE(called);
184}
185
186// Checks sending platform message responses works.
188 g_autoptr(FlEngine) engine = make_mock_engine();
190
191 bool called = false;
193 SendPlatformMessageResponse,
194 ([&called](auto engine,
196 const uint8_t* data, size_t data_length) {
197 called = true;
198
199 EXPECT_EQ(
200 handle,
201 reinterpret_cast<const FlutterPlatformMessageResponseHandle*>(42));
202 EXPECT_EQ(data_length, static_cast<size_t>(4));
203 EXPECT_EQ(data[0], 't');
204 EXPECT_EQ(data[1], 'e');
205 EXPECT_EQ(data[2], 's');
206 EXPECT_EQ(data[3], 't');
207
208 return kSuccess;
209 }));
210
211 g_autoptr(GError) error = nullptr;
213 EXPECT_EQ(error, nullptr);
214 g_autoptr(GBytes) response = g_bytes_new_static("test", 4);
216 engine, reinterpret_cast<const FlutterPlatformMessageResponseHandle*>(42),
217 response, &error));
218 EXPECT_EQ(error, nullptr);
219
220 EXPECT_TRUE(called);
221}
222
223// Checks settings plugin sends settings on startup.
224TEST(FlEngineTest, SettingsPlugin) {
225 g_autoptr(FlEngine) engine = make_mock_engine();
227
228 bool called = false;
230 SendPlatformMessage,
231 ([&called](auto engine, const FlutterPlatformMessage* message) {
232 called = true;
233
234 EXPECT_STREQ(message->channel, "flutter/settings");
235
236 g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
237 g_autoptr(GBytes) data =
238 g_bytes_new(message->message, message->message_size);
239 g_autoptr(GError) error = nullptr;
241 FL_MESSAGE_CODEC(codec), data, &error);
242 EXPECT_NE(settings, nullptr);
243 EXPECT_EQ(error, nullptr);
244
245 FlValue* text_scale_factor =
246 fl_value_lookup_string(settings, "textScaleFactor");
247 EXPECT_NE(text_scale_factor, nullptr);
248 EXPECT_EQ(fl_value_get_type(text_scale_factor), FL_VALUE_TYPE_FLOAT);
249
250 FlValue* always_use_24hr_format =
251 fl_value_lookup_string(settings, "alwaysUse24HourFormat");
252 EXPECT_NE(always_use_24hr_format, nullptr);
253 EXPECT_EQ(fl_value_get_type(always_use_24hr_format),
255
256 FlValue* platform_brightness =
257 fl_value_lookup_string(settings, "platformBrightness");
258 EXPECT_NE(platform_brightness, nullptr);
259 EXPECT_EQ(fl_value_get_type(platform_brightness), FL_VALUE_TYPE_STRING);
260
261 return kSuccess;
262 }));
263
264 g_autoptr(GError) error = nullptr;
266 EXPECT_EQ(error, nullptr);
267
268 EXPECT_TRUE(called);
269}
270
271void on_pre_engine_restart_cb(FlEngine* engine, gpointer user_data) {
272 int* count = reinterpret_cast<int*>(user_data);
273 *count += 1;
274}
275
277 int* count = reinterpret_cast<int*>(user_data);
278 *count += 10;
279}
280
281// Checks restarting the engine invokes the correct callback.
282TEST(FlEngineTest, OnPreEngineRestart) {
283 FlEngine* engine = make_mock_engine();
285
287 void* callback_user_data;
288
289 bool called = false;
290 embedder_api->Initialize = MOCK_ENGINE_PROC(
291 Initialize, ([&callback, &callback_user_data, &called](
292 size_t version, const FlutterRendererConfig* config,
293 const FlutterProjectArgs* args, void* user_data,
294 FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
295 called = true;
296 callback = args->on_pre_engine_restart_callback;
297 callback_user_data = user_data;
298
299 return kSuccess;
300 }));
301
302 g_autoptr(GError) error = nullptr;
304 EXPECT_EQ(error, nullptr);
305
306 EXPECT_TRUE(called);
307 EXPECT_NE(callback, nullptr);
308
309 // The following call has no effect but should not crash.
310 callback(callback_user_data);
311
312 int count = 0;
313
314 // Set handler so that:
315 //
316 // * When the engine restarts, count += 1;
317 // * When the engine is freed, count += 10.
321
322 callback(callback_user_data);
323 EXPECT_EQ(count, 1);
324
325 // Disposal should call the destroy notify.
326 g_object_unref(engine);
327 EXPECT_EQ(count, 11);
328}
329
330TEST(FlEngineTest, DartEntrypointArgs) {
331 g_autoptr(FlDartProject) project = fl_dart_project_new();
332
333 GPtrArray* args_array = g_ptr_array_new();
334 g_ptr_array_add(args_array, const_cast<char*>("arg_one"));
335 g_ptr_array_add(args_array, const_cast<char*>("arg_two"));
336 g_ptr_array_add(args_array, const_cast<char*>("arg_three"));
337 g_ptr_array_add(args_array, nullptr);
338 gchar** args = reinterpret_cast<gchar**>(g_ptr_array_free(args_array, false));
339
341
342 g_autoptr(FlEngine) engine = make_mock_engine_with_project(project);
344
345 bool called = false;
346 embedder_api->Initialize = MOCK_ENGINE_PROC(
347 Initialize, ([&called, &set_args = args](
348 size_t version, const FlutterRendererConfig* config,
349 const FlutterProjectArgs* args, void* user_data,
350 FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
351 called = true;
352 EXPECT_NE(set_args, args->dart_entrypoint_argv);
353 EXPECT_EQ(args->dart_entrypoint_argc, 3);
354
355 return kSuccess;
356 }));
357
358 g_autoptr(GError) error = nullptr;
360 EXPECT_EQ(error, nullptr);
361
362 EXPECT_TRUE(called);
363}
364
365TEST(FlEngineTest, Locales) {
366 gchar* initial_language = g_strdup(g_getenv("LANGUAGE"));
367 g_setenv("LANGUAGE", "de:en_US", TRUE);
368 g_autoptr(FlDartProject) project = fl_dart_project_new();
369
370 g_autoptr(FlEngine) engine = make_mock_engine_with_project(project);
372
373 bool called = false;
374 embedder_api->UpdateLocales = MOCK_ENGINE_PROC(
375 UpdateLocales, ([&called](auto engine, const FlutterLocale** locales,
376 size_t locales_count) {
377 called = true;
378
379 EXPECT_EQ(locales_count, static_cast<size_t>(4));
380
381 EXPECT_STREQ(locales[0]->language_code, "de");
382 EXPECT_STREQ(locales[0]->country_code, nullptr);
383 EXPECT_STREQ(locales[0]->script_code, nullptr);
384 EXPECT_STREQ(locales[0]->variant_code, nullptr);
385
386 EXPECT_STREQ(locales[1]->language_code, "en");
387 EXPECT_STREQ(locales[1]->country_code, "US");
388 EXPECT_STREQ(locales[1]->script_code, nullptr);
389 EXPECT_STREQ(locales[1]->variant_code, nullptr);
390
391 EXPECT_STREQ(locales[2]->language_code, "en");
392 EXPECT_STREQ(locales[2]->country_code, nullptr);
393 EXPECT_STREQ(locales[2]->script_code, nullptr);
394 EXPECT_STREQ(locales[2]->variant_code, nullptr);
395
396 EXPECT_STREQ(locales[3]->language_code, "C");
397 EXPECT_STREQ(locales[3]->country_code, nullptr);
398 EXPECT_STREQ(locales[3]->script_code, nullptr);
399 EXPECT_STREQ(locales[3]->variant_code, nullptr);
400
401 return kSuccess;
402 }));
403
404 g_autoptr(GError) error = nullptr;
406 EXPECT_EQ(error, nullptr);
407
408 EXPECT_TRUE(called);
409
410 if (initial_language) {
411 g_setenv("LANGUAGE", initial_language, TRUE);
412 } else {
413 g_unsetenv("LANGUAGE");
414 }
415 g_free(initial_language);
416}
417
418TEST(FlEngineTest, SwitchesEmpty) {
419 g_autoptr(FlEngine) engine = make_mock_engine();
420
421 // Clear the main environment variable, since test order is not guaranteed.
422 unsetenv("FLUTTER_ENGINE_SWITCHES");
423
424 g_autoptr(GPtrArray) switches = fl_engine_get_switches(engine);
425
426 EXPECT_EQ(switches->len, 0U);
427}
428
429TEST(FlEngineTest, SendWindowStateEvent) {
430 g_autoptr(FlEngine) engine = make_mock_engine();
432
433 bool called = false;
434 std::string state;
436 SendPlatformMessage,
437 ([&called, &state](auto engine, const FlutterPlatformMessage* message) {
438 EXPECT_STREQ(message->channel, "flutter/lifecycle");
439 called = true;
440 g_autoptr(FlStringCodec) codec = fl_string_codec_new();
441 g_autoptr(GBytes) data =
442 g_bytes_new(message->message, message->message_size);
443 g_autoptr(GError) error = nullptr;
444 g_autoptr(FlValue) parsed_state = fl_message_codec_decode_message(
445 FL_MESSAGE_CODEC(codec), data, &error);
446
447 state = fl_value_get_string(parsed_state);
448 return kSuccess;
449 }));
451 EXPECT_STREQ(state.c_str(), flutter::AppLifecycleStateToString(
454 EXPECT_STREQ(state.c_str(), flutter::AppLifecycleStateToString(
457 EXPECT_STREQ(state.c_str(), flutter::AppLifecycleStateToString(
460 EXPECT_STREQ(state.c_str(), flutter::AppLifecycleStateToString(
462 EXPECT_TRUE(called);
463}
464
465#ifndef FLUTTER_RELEASE
466TEST(FlEngineTest, Switches) {
467 g_autoptr(FlEngine) engine = make_mock_engine();
468
469 setenv("FLUTTER_ENGINE_SWITCHES", "2", 1);
470 setenv("FLUTTER_ENGINE_SWITCH_1", "abc", 1);
471 setenv("FLUTTER_ENGINE_SWITCH_2", "foo=\"bar, baz\"", 1);
472
473 g_autoptr(GPtrArray) switches = fl_engine_get_switches(engine);
474 EXPECT_EQ(switches->len, 2U);
475 EXPECT_STREQ(static_cast<const char*>(g_ptr_array_index(switches, 0)),
476 "--abc");
477 EXPECT_STREQ(static_cast<const char*>(g_ptr_array_index(switches, 1)),
478 "--foo=\"bar, baz\"");
479
480 unsetenv("FLUTTER_ENGINE_SWITCHES");
481 unsetenv("FLUTTER_ENGINE_SWITCH_1");
482 unsetenv("FLUTTER_ENGINE_SWITCH_2");
483}
484#endif // !FLUTTER_RELEASE
485
486// NOLINTEND(clang-analyzer-core.StackAddressEscape)
int count
Definition: FontMgrTest.cpp:50
struct _FlutterEngine * FLUTTER_API_SYMBOL(FlutterEngine)
Definition: embedder.h:269
@ kPanZoomUpdate
The pan/zoom updated.
Definition: embedder.h:1001
@ kDown
Definition: embedder.h:980
FlutterEngineResult(* FlutterEngineSendPlatformMessageFnPtr)(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage *message)
Definition: embedder.h:3227
@ kFlutterPointerButtonMouseSecondary
Definition: embedder.h:1018
@ kSuccess
Definition: embedder.h:73
@ kFlutterPointerSignalKindScroll
Definition: embedder.h:1029
@ kFlutterPointerSignalKindNone
Definition: embedder.h:1028
FlutterSemanticsAction
Definition: embedder.h:113
@ kFlutterSemanticsActionTap
Definition: embedder.h:116
@ kFlutterPointerDeviceKindTrackpad
Definition: embedder.h:1011
@ kFlutterPointerDeviceKindMouse
Definition: embedder.h:1008
void(* OnPreEngineRestartCallback)(void *)
Definition: embedder.h:420
VkDevice device
Definition: main.cc:53
FlutterEngine engine
Definition: main.cc:68
AtkStateType state
G_MODULE_EXPORT void fl_dart_project_set_dart_entrypoint_arguments(FlDartProject *self, char **argv)
G_MODULE_EXPORT FlDartProject * fl_dart_project_new()
void fl_engine_send_pointer_pan_zoom_event(FlEngine *self, size_t timestamp, double x, double y, FlutterPointerPhase phase, double pan_x, double pan_y, double scale, double rotation)
Definition: fl_engine.cc:826
gboolean fl_engine_send_platform_message_response(FlEngine *self, const FlutterPlatformMessageResponseHandle *handle, GBytes *response, GError **error)
Definition: fl_engine.cc:656
void fl_engine_set_on_pre_engine_restart_handler(FlEngine *self, FlEngineOnPreEngineRestartHandler handler, gpointer user_data, GDestroyNotify destroy_notify)
Definition: fl_engine.cc:638
void fl_engine_send_platform_message(FlEngine *self, const gchar *channel, GBytes *message, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Definition: fl_engine.cc:688
void fl_engine_dispatch_semantics_action(FlEngine *self, uint64_t id, FlutterSemanticsAction action, GBytes *data)
Definition: fl_engine.cc:873
GPtrArray * fl_engine_get_switches(FlEngine *self)
Definition: fl_engine.cc:948
void fl_engine_send_window_state_event(FlEngine *self, gboolean visible, gboolean focused)
Definition: fl_engine.cc:756
FlutterEngineProcTable * fl_engine_get_embedder_api(FlEngine *self)
Definition: fl_engine.cc:599
void fl_engine_send_window_metrics_event(FlEngine *self, size_t width, size_t height, double pixel_ratio)
Definition: fl_engine.cc:768
gboolean fl_engine_start(FlEngine *self, GError **error)
Definition: fl_engine.cc:471
void fl_engine_send_mouse_pointer_event(FlEngine *self, FlutterPointerPhase phase, size_t timestamp, double x, double y, FlutterPointerDeviceKind device_kind, double scroll_delta_x, double scroll_delta_y, int64_t buttons)
Definition: fl_engine.cc:790
void on_pre_engine_restart_cb(FlEngine *engine, gpointer user_data)
TEST(FlEngineTest, WindowMetrics)
void on_pre_engine_restart_destroy_notify(gpointer user_data)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static FlEngine * make_mock_engine()
G_MODULE_EXPORT FlJsonMessageCodec * fl_json_message_codec_new()
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event
G_MODULE_EXPORT FlValue * fl_message_codec_decode_message(FlMessageCodec *self, GBytes *message, GError **error)
const uint8_t uint32_t uint32_t GError ** error
G_MODULE_EXPORT FlStringCodec * fl_string_codec_new()
FlEngine * make_mock_engine_with_project(FlDartProject *project)
Definition: fl_test.cc:68
G_MODULE_EXPORT FlValue * fl_value_lookup_string(FlValue *self, const gchar *key)
Definition: fl_value.cc:811
G_MODULE_EXPORT FlValueType fl_value_get_type(FlValue *self)
Definition: fl_value.cc:466
G_MODULE_EXPORT const gchar * fl_value_get_string(FlValue *self)
Definition: fl_value.cc:682
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition: fl_value.h:42
@ FL_VALUE_TYPE_STRING
Definition: fl_value.h:69
@ FL_VALUE_TYPE_BOOL
Definition: fl_value.h:66
@ FL_VALUE_TYPE_FLOAT
Definition: fl_value.h:68
Win32Message message
double y
double x
constexpr const char * AppLifecycleStateToString(AppLifecycleState state)
static void DispatchSemanticsAction(JNIEnv *env, jobject jcaller, jlong shell_holder, jint id, jint action, jobject args, jint args_position)
void Initialize(zx::channel directory_request, std::optional< zx::eventpair > view_ref)
Initializes Dart bindings for the Fuchsia application model.
Definition: fuchsia.cc:103
#define MOCK_ENGINE_PROC(proc, mock_impl)
const Scalar scale
Function-pointer-based versions of the APIs above.
Definition: embedder.h:3319
FlutterEngineUpdateLocalesFnPtr UpdateLocales
Definition: embedder.h:3354
FlutterEngineSendWindowMetricsEventFnPtr SendWindowMetricsEvent
Definition: embedder.h:3330
FlutterEngineSendPointerEventFnPtr SendPointerEvent
Definition: embedder.h:3331
FlutterEngineInitializeFnPtr Initialize
Definition: embedder.h:3327
FlutterEngineDispatchSemanticsActionFnPtr DispatchSemanticsAction
Definition: embedder.h:3345
FlutterEngineSendPlatformMessageFnPtr SendPlatformMessage
Definition: embedder.h:3333
FlutterEngineSendPlatformMessageResponseFnPtr SendPlatformMessageResponse
Definition: embedder.h:3338
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
void * user_data
#define EXPECT_TRUE(handle)
Definition: unit_test.h:678