Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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, 1.2,
75
76 EXPECT_TRUE(called);
77}
78
79// Checks sending pan/zoom events works.
80TEST(FlEngineTest, PointerPanZoom) {
81 g_autoptr(FlEngine) engine = make_mock_engine();
83
84 bool called = false;
85 embedder_api->SendPointerEvent = MOCK_ENGINE_PROC(
86 SendPointerEvent,
87 ([&called](auto engine, const FlutterPointerEvent* events,
88 size_t events_count) {
89 called = true;
90 EXPECT_EQ(events_count, static_cast<size_t>(1));
91 EXPECT_EQ(events[0].phase, kPanZoomUpdate);
92 EXPECT_EQ(events[0].timestamp, static_cast<size_t>(1234567890));
93 EXPECT_EQ(events[0].x, 800);
94 EXPECT_EQ(events[0].y, 600);
95 EXPECT_EQ(events[0].device, static_cast<int32_t>(1));
96 EXPECT_EQ(events[0].signal_kind, kFlutterPointerSignalKindNone);
97 EXPECT_EQ(events[0].pan_x, 1.5);
98 EXPECT_EQ(events[0].pan_y, 2.5);
99 EXPECT_EQ(events[0].scale, 3.5);
100 EXPECT_EQ(events[0].rotation, 4.5);
101 EXPECT_EQ(events[0].device_kind, kFlutterPointerDeviceKindTrackpad);
102 EXPECT_EQ(events[0].buttons, 0);
103
104 return kSuccess;
105 }));
106
107 g_autoptr(GError) error = nullptr;
109 EXPECT_EQ(error, nullptr);
110 fl_engine_send_pointer_pan_zoom_event(engine, 1234567890, 800, 600,
111 kPanZoomUpdate, 1.5, 2.5, 3.5, 4.5);
112
113 EXPECT_TRUE(called);
114}
115
116// Checks dispatching a semantics action works.
117TEST(FlEngineTest, DispatchSemanticsAction) {
118 g_autoptr(FlEngine) engine = make_mock_engine();
120
121 bool called = false;
123 DispatchSemanticsAction,
124 ([&called](auto engine, uint64_t id, FlutterSemanticsAction action,
125 const uint8_t* data, size_t data_length) {
126 EXPECT_EQ(id, static_cast<uint64_t>(42));
128 EXPECT_EQ(data_length, static_cast<size_t>(4));
129 EXPECT_EQ(data[0], 't');
130 EXPECT_EQ(data[1], 'e');
131 EXPECT_EQ(data[2], 's');
132 EXPECT_EQ(data[3], 't');
133 called = true;
134
135 return kSuccess;
136 }));
137
138 g_autoptr(GError) error = nullptr;
140 EXPECT_EQ(error, nullptr);
141 g_autoptr(GBytes) data = g_bytes_new_static("test", 4);
143 data);
144
145 EXPECT_TRUE(called);
146}
147
148// Checks sending platform messages works.
149TEST(FlEngineTest, PlatformMessage) {
150 g_autoptr(FlEngine) engine = make_mock_engine();
152
153 bool called = false;
155 embedder_api->SendPlatformMessage;
157 SendPlatformMessage,
158 ([&called, old_handler](auto engine,
160 if (strcmp(message->channel, "test") != 0) {
161 return old_handler(engine, message);
162 }
163
164 called = true;
165
166 EXPECT_EQ(message->message_size, static_cast<size_t>(4));
167 EXPECT_EQ(message->message[0], 't');
168 EXPECT_EQ(message->message[1], 'e');
169 EXPECT_EQ(message->message[2], 's');
170 EXPECT_EQ(message->message[3], 't');
171
172 return kSuccess;
173 }));
174
175 g_autoptr(GError) error = nullptr;
177 EXPECT_EQ(error, nullptr);
178 g_autoptr(GBytes) message = g_bytes_new_static("test", 4);
179 fl_engine_send_platform_message(engine, "test", message, nullptr, nullptr,
180 nullptr);
181
182 EXPECT_TRUE(called);
183}
184
185// Checks sending platform message responses works.
187 g_autoptr(FlEngine) engine = make_mock_engine();
189
190 bool called = false;
192 SendPlatformMessageResponse,
193 ([&called](auto engine,
195 const uint8_t* data, size_t data_length) {
196 called = true;
197
198 EXPECT_EQ(
199 handle,
200 reinterpret_cast<const FlutterPlatformMessageResponseHandle*>(42));
201 EXPECT_EQ(data_length, static_cast<size_t>(4));
202 EXPECT_EQ(data[0], 't');
203 EXPECT_EQ(data[1], 'e');
204 EXPECT_EQ(data[2], 's');
205 EXPECT_EQ(data[3], 't');
206
207 return kSuccess;
208 }));
209
210 g_autoptr(GError) error = nullptr;
212 EXPECT_EQ(error, nullptr);
213 g_autoptr(GBytes) response = g_bytes_new_static("test", 4);
215 engine, reinterpret_cast<const FlutterPlatformMessageResponseHandle*>(42),
216 response, &error));
217 EXPECT_EQ(error, nullptr);
218
219 EXPECT_TRUE(called);
220}
221
222// Checks settings plugin sends settings on startup.
223TEST(FlEngineTest, SettingsPlugin) {
224 g_autoptr(FlEngine) engine = make_mock_engine();
226
227 bool called = false;
229 SendPlatformMessage,
230 ([&called](auto engine, const FlutterPlatformMessage* message) {
231 called = true;
232
233 EXPECT_STREQ(message->channel, "flutter/settings");
234
235 g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
236 g_autoptr(GBytes) data =
237 g_bytes_new(message->message, message->message_size);
238 g_autoptr(GError) error = nullptr;
239 g_autoptr(FlValue) settings = fl_message_codec_decode_message(
240 FL_MESSAGE_CODEC(codec), data, &error);
241 EXPECT_NE(settings, nullptr);
242 EXPECT_EQ(error, nullptr);
243
244 FlValue* text_scale_factor =
245 fl_value_lookup_string(settings, "textScaleFactor");
246 EXPECT_NE(text_scale_factor, nullptr);
247 EXPECT_EQ(fl_value_get_type(text_scale_factor), FL_VALUE_TYPE_FLOAT);
248
249 FlValue* always_use_24hr_format =
250 fl_value_lookup_string(settings, "alwaysUse24HourFormat");
251 EXPECT_NE(always_use_24hr_format, nullptr);
252 EXPECT_EQ(fl_value_get_type(always_use_24hr_format),
254
255 FlValue* platform_brightness =
256 fl_value_lookup_string(settings, "platformBrightness");
257 EXPECT_NE(platform_brightness, nullptr);
258 EXPECT_EQ(fl_value_get_type(platform_brightness), FL_VALUE_TYPE_STRING);
259
260 return kSuccess;
261 }));
262
263 g_autoptr(GError) error = nullptr;
265 EXPECT_EQ(error, nullptr);
266
267 EXPECT_TRUE(called);
268}
269
270void on_pre_engine_restart_cb(FlEngine* engine, gpointer user_data) {
271 int* count = reinterpret_cast<int*>(user_data);
272 *count += 1;
273}
274
276 int* count = reinterpret_cast<int*>(user_data);
277 *count += 10;
278}
279
280// Checks restarting the engine invokes the correct callback.
281TEST(FlEngineTest, OnPreEngineRestart) {
282 FlEngine* engine = make_mock_engine();
284
286 void* callback_user_data;
287
288 bool called = false;
289 embedder_api->Initialize = MOCK_ENGINE_PROC(
290 Initialize, ([&callback, &callback_user_data, &called](
291 size_t version, const FlutterRendererConfig* config,
292 const FlutterProjectArgs* args, void* user_data,
293 FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
294 called = true;
295 callback = args->on_pre_engine_restart_callback;
296 callback_user_data = user_data;
297
298 return kSuccess;
299 }));
300
301 g_autoptr(GError) error = nullptr;
303 EXPECT_EQ(error, nullptr);
304
305 EXPECT_TRUE(called);
306 EXPECT_NE(callback, nullptr);
307
308 // The following call has no effect but should not crash.
309 callback(callback_user_data);
310
311 int count = 0;
312
313 // Set handler so that:
314 //
315 // * When the engine restarts, count += 1;
316 // * When the engine is freed, count += 10.
320
321 callback(callback_user_data);
322 EXPECT_EQ(count, 1);
323
324 // Disposal should call the destroy notify.
325 g_object_unref(engine);
326 EXPECT_EQ(count, 11);
327}
328
329TEST(FlEngineTest, DartEntrypointArgs) {
330 g_autoptr(FlDartProject) project = fl_dart_project_new();
331
332 GPtrArray* args_array = g_ptr_array_new();
333 g_ptr_array_add(args_array, const_cast<char*>("arg_one"));
334 g_ptr_array_add(args_array, const_cast<char*>("arg_two"));
335 g_ptr_array_add(args_array, const_cast<char*>("arg_three"));
336 g_ptr_array_add(args_array, nullptr);
337 gchar** args = reinterpret_cast<gchar**>(g_ptr_array_free(args_array, false));
338
340
341 g_autoptr(FlEngine) engine = make_mock_engine_with_project(project);
343
344 bool called = false;
345 embedder_api->Initialize = MOCK_ENGINE_PROC(
346 Initialize, ([&called, &set_args = args](
347 size_t version, const FlutterRendererConfig* config,
348 const FlutterProjectArgs* args, void* user_data,
349 FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
350 called = true;
351 EXPECT_NE(set_args, args->dart_entrypoint_argv);
352 EXPECT_EQ(args->dart_entrypoint_argc, 3);
353
354 return kSuccess;
355 }));
356
357 g_autoptr(GError) error = nullptr;
359 EXPECT_EQ(error, nullptr);
360
361 EXPECT_TRUE(called);
362}
363
364TEST(FlEngineTest, Locales) {
365 gchar* initial_language = g_strdup(g_getenv("LANGUAGE"));
366 g_setenv("LANGUAGE", "de:en_US", TRUE);
367 g_autoptr(FlDartProject) project = fl_dart_project_new();
368
369 g_autoptr(FlEngine) engine = make_mock_engine_with_project(project);
371
372 bool called = false;
373 embedder_api->UpdateLocales = MOCK_ENGINE_PROC(
374 UpdateLocales, ([&called](auto engine, const FlutterLocale** locales,
375 size_t locales_count) {
376 called = true;
377
378 EXPECT_EQ(locales_count, static_cast<size_t>(4));
379
380 EXPECT_STREQ(locales[0]->language_code, "de");
381 EXPECT_STREQ(locales[0]->country_code, nullptr);
382 EXPECT_STREQ(locales[0]->script_code, nullptr);
383 EXPECT_STREQ(locales[0]->variant_code, nullptr);
384
385 EXPECT_STREQ(locales[1]->language_code, "en");
386 EXPECT_STREQ(locales[1]->country_code, "US");
387 EXPECT_STREQ(locales[1]->script_code, nullptr);
388 EXPECT_STREQ(locales[1]->variant_code, nullptr);
389
390 EXPECT_STREQ(locales[2]->language_code, "en");
391 EXPECT_STREQ(locales[2]->country_code, nullptr);
392 EXPECT_STREQ(locales[2]->script_code, nullptr);
393 EXPECT_STREQ(locales[2]->variant_code, nullptr);
394
395 EXPECT_STREQ(locales[3]->language_code, "C");
396 EXPECT_STREQ(locales[3]->country_code, nullptr);
397 EXPECT_STREQ(locales[3]->script_code, nullptr);
398 EXPECT_STREQ(locales[3]->variant_code, nullptr);
399
400 return kSuccess;
401 }));
402
403 g_autoptr(GError) error = nullptr;
405 EXPECT_EQ(error, nullptr);
406
407 EXPECT_TRUE(called);
408
409 if (initial_language) {
410 g_setenv("LANGUAGE", initial_language, TRUE);
411 } else {
412 g_unsetenv("LANGUAGE");
413 }
414 g_free(initial_language);
415}
416
417TEST(FlEngineTest, SwitchesEmpty) {
418 g_autoptr(FlEngine) engine = make_mock_engine();
419
420 // Clear the main environment variable, since test order is not guaranteed.
421 unsetenv("FLUTTER_ENGINE_SWITCHES");
422
423 g_autoptr(GPtrArray) switches = fl_engine_get_switches(engine);
424
425 EXPECT_EQ(switches->len, 0U);
426}
427
428TEST(FlEngineTest, SendWindowStateEvent) {
429 g_autoptr(FlEngine) engine = make_mock_engine();
431
432 bool called = false;
433 std::string state;
435 SendPlatformMessage,
436 ([&called, &state](auto engine, const FlutterPlatformMessage* message) {
437 EXPECT_STREQ(message->channel, "flutter/lifecycle");
438 called = true;
439 g_autoptr(FlStringCodec) codec = fl_string_codec_new();
440 g_autoptr(GBytes) data =
441 g_bytes_new(message->message, message->message_size);
442 g_autoptr(GError) error = nullptr;
443 g_autoptr(FlValue) parsed_state = fl_message_codec_decode_message(
444 FL_MESSAGE_CODEC(codec), data, &error);
445
446 state = fl_value_get_string(parsed_state);
447 return kSuccess;
448 }));
450 EXPECT_STREQ(state.c_str(), flutter::AppLifecycleStateToString(
453 EXPECT_STREQ(state.c_str(), flutter::AppLifecycleStateToString(
456 EXPECT_STREQ(state.c_str(), flutter::AppLifecycleStateToString(
459 EXPECT_STREQ(state.c_str(), flutter::AppLifecycleStateToString(
461 EXPECT_TRUE(called);
462}
463
464#ifndef FLUTTER_RELEASE
465TEST(FlEngineTest, Switches) {
466 g_autoptr(FlEngine) engine = make_mock_engine();
467
468 setenv("FLUTTER_ENGINE_SWITCHES", "2", 1);
469 setenv("FLUTTER_ENGINE_SWITCH_1", "abc", 1);
470 setenv("FLUTTER_ENGINE_SWITCH_2", "foo=\"bar, baz\"", 1);
471
472 g_autoptr(GPtrArray) switches = fl_engine_get_switches(engine);
473 EXPECT_EQ(switches->len, 2U);
474 EXPECT_STREQ(static_cast<const char*>(g_ptr_array_index(switches, 0)),
475 "--abc");
476 EXPECT_STREQ(static_cast<const char*>(g_ptr_array_index(switches, 1)),
477 "--foo=\"bar, baz\"");
478
479 unsetenv("FLUTTER_ENGINE_SWITCHES");
480 unsetenv("FLUTTER_ENGINE_SWITCH_1");
481 unsetenv("FLUTTER_ENGINE_SWITCH_2");
482}
483#endif // !FLUTTER_RELEASE
484
485// NOLINTEND(clang-analyzer-core.StackAddressEscape)
#define TEST(S, s, D, expected)
int count
#define FLUTTER_API_SYMBOL(symbol)
Definition embedder.h:67
@ kPanZoomUpdate
The pan/zoom updated.
Definition embedder.h:999
@ kDown
Definition embedder.h:978
FlutterEngineResult(* FlutterEngineSendPlatformMessageFnPtr)(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessage *message)
Definition embedder.h:3225
@ kFlutterPointerButtonMouseSecondary
Definition embedder.h:1016
@ kSuccess
Definition embedder.h:73
@ kFlutterPointerSignalKindScroll
Definition embedder.h:1027
@ kFlutterPointerSignalKindNone
Definition embedder.h:1026
FlutterSemanticsAction
Definition embedder.h:113
@ kFlutterSemanticsActionTap
Definition embedder.h:116
@ kFlutterPointerDeviceKindTrackpad
Definition embedder.h:1009
@ kFlutterPointerDeviceKindMouse
Definition embedder.h:1006
void(* OnPreEngineRestartCallback)(void *)
Definition embedder.h:418
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:805
gboolean fl_engine_send_platform_message_response(FlEngine *self, const FlutterPlatformMessageResponseHandle *handle, GBytes *response, GError **error)
Definition fl_engine.cc:636
void fl_engine_set_on_pre_engine_restart_handler(FlEngine *self, FlEngineOnPreEngineRestartHandler handler, gpointer user_data, GDestroyNotify destroy_notify)
Definition fl_engine.cc:618
void fl_engine_send_platform_message(FlEngine *self, const gchar *channel, GBytes *message, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Definition fl_engine.cc:668
void fl_engine_dispatch_semantics_action(FlEngine *self, uint64_t id, FlutterSemanticsAction action, GBytes *data)
Definition fl_engine.cc:852
GPtrArray * fl_engine_get_switches(FlEngine *self)
Definition fl_engine.cc:927
void fl_engine_send_window_state_event(FlEngine *self, gboolean visible, gboolean focused)
Definition fl_engine.cc:736
void fl_engine_send_mouse_pointer_event(FlEngine *self, FlutterPointerPhase phase, size_t timestamp, double x, double y, double scroll_delta_x, double scroll_delta_y, int64_t buttons)
Definition fl_engine.cc:770
FlutterEngineProcTable * fl_engine_get_embedder_api(FlEngine *self)
Definition fl_engine.cc:579
void fl_engine_send_window_metrics_event(FlEngine *self, size_t width, size_t height, double pixel_ratio)
Definition fl_engine.cc:748
gboolean fl_engine_start(FlEngine *self, GError **error)
Definition fl_engine.cc:471
void on_pre_engine_restart_cb(FlEngine *engine, gpointer user_data)
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)
#define MOCK_ENGINE_PROC(proc, mock_impl)
const Scalar scale
Function-pointer-based versions of the APIs above.
Definition embedder.h:3317
FlutterEngineUpdateLocalesFnPtr UpdateLocales
Definition embedder.h:3352
FlutterEngineSendWindowMetricsEventFnPtr SendWindowMetricsEvent
Definition embedder.h:3328
FlutterEngineSendPointerEventFnPtr SendPointerEvent
Definition embedder.h:3329
FlutterEngineInitializeFnPtr Initialize
Definition embedder.h:3325
FlutterEngineDispatchSemanticsActionFnPtr DispatchSemanticsAction
Definition embedder.h:3343
FlutterEngineSendPlatformMessageFnPtr SendPlatformMessage
Definition embedder.h:3331
FlutterEngineSendPlatformMessageResponseFnPtr SendPlatformMessageResponse
Definition embedder.h:3336
#define EXPECT_TRUE(handle)
Definition unit_test.h:685