| Flutter Linux Embedder |

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/embedder/test_utils/proc_table_replacement.h"
9 #include "flutter/shell/platform/linux/fl_engine_private.h"
10 #include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
11 #include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
12 #include "flutter/shell/platform/linux/public/flutter_linux/fl_string_codec.h"
13 #include "flutter/shell/platform/linux/testing/fl_test.h"
14
15 // MOCK_ENGINE_PROC is leaky by design
16 // NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
17
18 // Checks sending window metrics events works.
19 TEST(FlEngineTest, WindowMetrics) {
20 g_autoptr(FlEngine) engine = make_mock_engine();
21 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
22
23 bool called = false;
24 embedder_api->SendWindowMetricsEvent = MOCK_ENGINE_PROC(
25 SendWindowMetricsEvent,
26 ([&called](auto engine, const FlutterWindowMetricsEvent* event) {
27 called = true;
28 EXPECT_EQ(event->view_id, 1);
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;
37 EXPECT_TRUE(fl_engine_start(engine, &error));
38 EXPECT_EQ(error, nullptr);
39 fl_engine_send_window_metrics_event(engine, 1, 3840, 2160, 2.0);
40
41 EXPECT_TRUE(called);
42 }
43
44 // Checks sending mouse pointer events works.
45 TEST(FlEngineTest, MousePointer) {
46 g_autoptr(FlEngine) engine = make_mock_engine();
47 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(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].view_id, 1);
57 EXPECT_EQ(events[0].phase, kDown);
58 EXPECT_EQ(events[0].timestamp, static_cast<size_t>(1234567890));
59 EXPECT_EQ(events[0].x, 800);
60 EXPECT_EQ(events[0].y, 600);
61 EXPECT_EQ(events[0].device, static_cast<int32_t>(0));
62 EXPECT_EQ(events[0].signal_kind, kFlutterPointerSignalKindScroll);
63 EXPECT_EQ(events[0].scroll_delta_x, 1.2);
64 EXPECT_EQ(events[0].scroll_delta_y, -3.4);
65 EXPECT_EQ(events[0].device_kind, kFlutterPointerDeviceKindMouse);
66 EXPECT_EQ(events[0].buttons, kFlutterPointerButtonMouseSecondary);
67
68 return kSuccess;
69 }));
70
71 g_autoptr(GError) error = nullptr;
72 EXPECT_TRUE(fl_engine_start(engine, &error));
73 EXPECT_EQ(error, nullptr);
74 fl_engine_send_mouse_pointer_event(engine, 1, kDown, 1234567890, 800, 600,
75 kFlutterPointerDeviceKindMouse, 1.2, -3.4,
76 kFlutterPointerButtonMouseSecondary);
77
78 EXPECT_TRUE(called);
79 }
80
81 // Checks sending pan/zoom events works.
82 TEST(FlEngineTest, PointerPanZoom) {
83 g_autoptr(FlEngine) engine = make_mock_engine();
84 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
85
86 bool called = false;
87 embedder_api->SendPointerEvent = MOCK_ENGINE_PROC(
88 SendPointerEvent,
89 ([&called](auto engine, const FlutterPointerEvent* events,
90 size_t events_count) {
91 called = true;
92 EXPECT_EQ(events_count, static_cast<size_t>(1));
93 EXPECT_EQ(events[0].view_id, 1);
94 EXPECT_EQ(events[0].phase, kPanZoomUpdate);
95 EXPECT_EQ(events[0].timestamp, static_cast<size_t>(1234567890));
96 EXPECT_EQ(events[0].x, 800);
97 EXPECT_EQ(events[0].y, 600);
98 EXPECT_EQ(events[0].device, static_cast<int32_t>(1));
99 EXPECT_EQ(events[0].signal_kind, kFlutterPointerSignalKindNone);
100 EXPECT_EQ(events[0].pan_x, 1.5);
101 EXPECT_EQ(events[0].pan_y, 2.5);
102 EXPECT_EQ(events[0].scale, 3.5);
103 EXPECT_EQ(events[0].rotation, 4.5);
104 EXPECT_EQ(events[0].device_kind, kFlutterPointerDeviceKindTrackpad);
105 EXPECT_EQ(events[0].buttons, 0);
106
107 return kSuccess;
108 }));
109
110 g_autoptr(GError) error = nullptr;
111 EXPECT_TRUE(fl_engine_start(engine, &error));
112 EXPECT_EQ(error, nullptr);
113 fl_engine_send_pointer_pan_zoom_event(engine, 1, 1234567890, 800, 600,
114 kPanZoomUpdate, 1.5, 2.5, 3.5, 4.5);
115
116 EXPECT_TRUE(called);
117 }
118
119 // Checks dispatching a semantics action works.
120 TEST(FlEngineTest, DispatchSemanticsAction) {
121 g_autoptr(FlEngine) engine = make_mock_engine();
122 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
123
124 bool called = false;
125 embedder_api->DispatchSemanticsAction = MOCK_ENGINE_PROC(
126 DispatchSemanticsAction,
127 ([&called](auto engine, uint64_t id, FlutterSemanticsAction action,
128 const uint8_t* data, size_t data_length) {
129 EXPECT_EQ(id, static_cast<uint64_t>(42));
130 EXPECT_EQ(action, kFlutterSemanticsActionTap);
131 EXPECT_EQ(data_length, static_cast<size_t>(4));
132 EXPECT_EQ(data[0], 't');
133 EXPECT_EQ(data[1], 'e');
134 EXPECT_EQ(data[2], 's');
135 EXPECT_EQ(data[3], 't');
136 called = true;
137
138 return kSuccess;
139 }));
140
141 g_autoptr(GError) error = nullptr;
142 EXPECT_TRUE(fl_engine_start(engine, &error));
143 EXPECT_EQ(error, nullptr);
144 g_autoptr(GBytes) data = g_bytes_new_static("test", 4);
145 fl_engine_dispatch_semantics_action(engine, 42, kFlutterSemanticsActionTap,
146 data);
147
148 EXPECT_TRUE(called);
149 }
150
151 // Checks sending platform messages works.
152 TEST(FlEngineTest, PlatformMessage) {
153 g_autoptr(FlEngine) engine = make_mock_engine();
154 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
155
156 bool called = false;
157 FlutterEngineSendPlatformMessageFnPtr old_handler =
158 embedder_api->SendPlatformMessage;
159 embedder_api->SendPlatformMessage = MOCK_ENGINE_PROC(
160 SendPlatformMessage,
161 ([&called, old_handler](auto engine,
162 const FlutterPlatformMessage* message) {
163 if (strcmp(message->channel, "test") != 0) {
164 return old_handler(engine, message);
165 }
166
167 called = true;
168
169 EXPECT_EQ(message->message_size, static_cast<size_t>(4));
170 EXPECT_EQ(message->message[0], 't');
171 EXPECT_EQ(message->message[1], 'e');
172 EXPECT_EQ(message->message[2], 's');
173 EXPECT_EQ(message->message[3], 't');
174
175 return kSuccess;
176 }));
177
178 g_autoptr(GError) error = nullptr;
179 EXPECT_TRUE(fl_engine_start(engine, &error));
180 EXPECT_EQ(error, nullptr);
181 g_autoptr(GBytes) message = g_bytes_new_static("test", 4);
182 fl_engine_send_platform_message(engine, "test", message, nullptr, nullptr,
183 nullptr);
184
185 EXPECT_TRUE(called);
186 }
187
188 // Checks sending platform message responses works.
189 TEST(FlEngineTest, PlatformMessageResponse) {
190 g_autoptr(FlEngine) engine = make_mock_engine();
191 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
192
193 bool called = false;
194 embedder_api->SendPlatformMessageResponse = MOCK_ENGINE_PROC(
195 SendPlatformMessageResponse,
196 ([&called](auto engine,
197 const FlutterPlatformMessageResponseHandle* handle,
198 const uint8_t* data, size_t data_length) {
199 called = true;
200
201 EXPECT_EQ(
202 handle,
203 reinterpret_cast<const FlutterPlatformMessageResponseHandle*>(42));
204 EXPECT_EQ(data_length, static_cast<size_t>(4));
205 EXPECT_EQ(data[0], 't');
206 EXPECT_EQ(data[1], 'e');
207 EXPECT_EQ(data[2], 's');
208 EXPECT_EQ(data[3], 't');
209
210 return kSuccess;
211 }));
212
213 g_autoptr(GError) error = nullptr;
214 EXPECT_TRUE(fl_engine_start(engine, &error));
215 EXPECT_EQ(error, nullptr);
216 g_autoptr(GBytes) response = g_bytes_new_static("test", 4);
217 EXPECT_TRUE(fl_engine_send_platform_message_response(
218 engine, reinterpret_cast<const FlutterPlatformMessageResponseHandle*>(42),
219 response, &error));
220 EXPECT_EQ(error, nullptr);
221
222 EXPECT_TRUE(called);
223 }
224
225 // Checks settings handler sends settings on startup.
226 TEST(FlEngineTest, SettingsHandler) {
227 g_autoptr(FlEngine) engine = make_mock_engine();
228 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
229
230 bool called = false;
231 embedder_api->SendPlatformMessage = MOCK_ENGINE_PROC(
232 SendPlatformMessage,
233 ([&called](auto engine, const FlutterPlatformMessage* message) {
234 called = true;
235
236 EXPECT_STREQ(message->channel, "flutter/settings");
237
238 g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
239 g_autoptr(GBytes) data =
240 g_bytes_new(message->message, message->message_size);
241 g_autoptr(GError) error = nullptr;
242 g_autoptr(FlValue) settings = fl_message_codec_decode_message(
243 FL_MESSAGE_CODEC(codec), data, &error);
244 EXPECT_NE(settings, nullptr);
245 EXPECT_EQ(error, nullptr);
246
247 FlValue* text_scale_factor =
248 fl_value_lookup_string(settings, "textScaleFactor");
249 EXPECT_NE(text_scale_factor, nullptr);
250 EXPECT_EQ(fl_value_get_type(text_scale_factor), FL_VALUE_TYPE_FLOAT);
251
252 FlValue* always_use_24hr_format =
253 fl_value_lookup_string(settings, "alwaysUse24HourFormat");
254 EXPECT_NE(always_use_24hr_format, nullptr);
255 EXPECT_EQ(fl_value_get_type(always_use_24hr_format),
256 FL_VALUE_TYPE_BOOL);
257
258 FlValue* platform_brightness =
259 fl_value_lookup_string(settings, "platformBrightness");
260 EXPECT_NE(platform_brightness, nullptr);
261 EXPECT_EQ(fl_value_get_type(platform_brightness), FL_VALUE_TYPE_STRING);
262
263 return kSuccess;
264 }));
265
266 g_autoptr(GError) error = nullptr;
267 EXPECT_TRUE(fl_engine_start(engine, &error));
268 EXPECT_EQ(error, nullptr);
269
270 EXPECT_TRUE(called);
271 }
272
273 void on_pre_engine_restart_cb(FlEngine* engine, gpointer user_data) {
274 int* count = reinterpret_cast<int*>(user_data);
275 *count += 1;
276 }
277
278 // Checks restarting the engine invokes the correct callback.
279 TEST(FlEngineTest, OnPreEngineRestart) {
280 g_autoptr(FlEngine) engine = make_mock_engine();
281 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
282
283 OnPreEngineRestartCallback callback;
284 void* callback_user_data;
285
286 bool called = false;
287 embedder_api->Initialize = MOCK_ENGINE_PROC(
288 Initialize, ([&callback, &callback_user_data, &called](
289 size_t version, const FlutterRendererConfig* config,
290 const FlutterProjectArgs* args, void* user_data,
291 FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
292 called = true;
293 callback = args->on_pre_engine_restart_callback;
294 callback_user_data = user_data;
295
296 return kSuccess;
297 }));
298
299 g_autoptr(GError) error = nullptr;
300 EXPECT_TRUE(fl_engine_start(engine, &error));
301 EXPECT_EQ(error, nullptr);
302
303 EXPECT_TRUE(called);
304 EXPECT_NE(callback, nullptr);
305
306 // The following call has no effect but should not crash.
307 callback(callback_user_data);
308
309 int count = 0;
310
311 // Set handler so that:
312 //
313 // * When the engine restarts, count += 1;
314 // * When the engine is freed, count += 10.
315 g_signal_connect(engine, "on-pre-engine-restart",
316 G_CALLBACK(on_pre_engine_restart_cb), &count);
317
318 callback(callback_user_data);
319 EXPECT_EQ(count, 1);
320 }
321
322 TEST(FlEngineTest, DartEntrypointArgs) {
323 g_autoptr(FlDartProject) project = fl_dart_project_new();
324
325 GPtrArray* args_array = g_ptr_array_new();
326 g_ptr_array_add(args_array, const_cast<char*>("arg_one"));
327 g_ptr_array_add(args_array, const_cast<char*>("arg_two"));
328 g_ptr_array_add(args_array, const_cast<char*>("arg_three"));
329 g_ptr_array_add(args_array, nullptr);
330 gchar** args = reinterpret_cast<gchar**>(g_ptr_array_free(args_array, false));
331
332 fl_dart_project_set_dart_entrypoint_arguments(project, args);
333
334 g_autoptr(FlEngine) engine = make_mock_engine_with_project(project);
335 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
336
337 bool called = false;
338 embedder_api->Initialize = MOCK_ENGINE_PROC(
339 Initialize, ([&called, &set_args = args](
340 size_t version, const FlutterRendererConfig* config,
341 const FlutterProjectArgs* args, void* user_data,
342 FLUTTER_API_SYMBOL(FlutterEngine) * engine_out) {
343 called = true;
344 EXPECT_NE(set_args, args->dart_entrypoint_argv);
345 EXPECT_EQ(args->dart_entrypoint_argc, 3);
346
347 return kSuccess;
348 }));
349
350 g_autoptr(GError) error = nullptr;
351 EXPECT_TRUE(fl_engine_start(engine, &error));
352 EXPECT_EQ(error, nullptr);
353
354 EXPECT_TRUE(called);
355 }
356
357 TEST(FlEngineTest, Locales) {
358 g_autofree gchar* initial_language = g_strdup(g_getenv("LANGUAGE"));
359 g_setenv("LANGUAGE", "de:en_US", TRUE);
360 g_autoptr(FlDartProject) project = fl_dart_project_new();
361
362 g_autoptr(FlEngine) engine = make_mock_engine_with_project(project);
363 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
364
365 bool called = false;
366 embedder_api->UpdateLocales = MOCK_ENGINE_PROC(
367 UpdateLocales, ([&called](auto engine, const FlutterLocale** locales,
368 size_t locales_count) {
369 called = true;
370
371 EXPECT_EQ(locales_count, static_cast<size_t>(4));
372
373 EXPECT_STREQ(locales[0]->language_code, "de");
374 EXPECT_STREQ(locales[0]->country_code, nullptr);
375 EXPECT_STREQ(locales[0]->script_code, nullptr);
376 EXPECT_STREQ(locales[0]->variant_code, nullptr);
377
378 EXPECT_STREQ(locales[1]->language_code, "en");
379 EXPECT_STREQ(locales[1]->country_code, "US");
380 EXPECT_STREQ(locales[1]->script_code, nullptr);
381 EXPECT_STREQ(locales[1]->variant_code, nullptr);
382
383 EXPECT_STREQ(locales[2]->language_code, "en");
384 EXPECT_STREQ(locales[2]->country_code, nullptr);
385 EXPECT_STREQ(locales[2]->script_code, nullptr);
386 EXPECT_STREQ(locales[2]->variant_code, nullptr);
387
388 EXPECT_STREQ(locales[3]->language_code, "C");
389 EXPECT_STREQ(locales[3]->country_code, nullptr);
390 EXPECT_STREQ(locales[3]->script_code, nullptr);
391 EXPECT_STREQ(locales[3]->variant_code, nullptr);
392
393 return kSuccess;
394 }));
395
396 g_autoptr(GError) error = nullptr;
397 EXPECT_TRUE(fl_engine_start(engine, &error));
398 EXPECT_EQ(error, nullptr);
399
400 EXPECT_TRUE(called);
401
402 if (initial_language) {
403 g_setenv("LANGUAGE", initial_language, TRUE);
404 } else {
405 g_unsetenv("LANGUAGE");
406 }
407 }
408
409 TEST(FlEngineTest, CLocale) {
410 g_autofree gchar* initial_language = g_strdup(g_getenv("LANGUAGE"));
411 g_setenv("LANGUAGE", "C", TRUE);
412 g_autoptr(FlDartProject) project = fl_dart_project_new();
413
414 g_autoptr(FlEngine) engine = make_mock_engine_with_project(project);
415 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
416
417 bool called = false;
418 embedder_api->UpdateLocales = MOCK_ENGINE_PROC(
419 UpdateLocales, ([&called](auto engine, const FlutterLocale** locales,
420 size_t locales_count) {
421 called = true;
422
423 EXPECT_EQ(locales_count, static_cast<size_t>(1));
424
425 EXPECT_STREQ(locales[0]->language_code, "C");
426 EXPECT_STREQ(locales[0]->country_code, nullptr);
427 EXPECT_STREQ(locales[0]->script_code, nullptr);
428 EXPECT_STREQ(locales[0]->variant_code, nullptr);
429
430 return kSuccess;
431 }));
432
433 g_autoptr(GError) error = nullptr;
434 EXPECT_TRUE(fl_engine_start(engine, &error));
435 EXPECT_EQ(error, nullptr);
436
437 EXPECT_TRUE(called);
438
439 if (initial_language) {
440 g_setenv("LANGUAGE", initial_language, TRUE);
441 } else {
442 g_unsetenv("LANGUAGE");
443 }
444 }
445
446 TEST(FlEngineTest, DuplicateLocale) {
447 g_autofree gchar* initial_language = g_strdup(g_getenv("LANGUAGE"));
448 g_setenv("LANGUAGE", "en:en", TRUE);
449 g_autoptr(FlDartProject) project = fl_dart_project_new();
450
451 g_autoptr(FlEngine) engine = make_mock_engine_with_project(project);
452 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
453
454 bool called = false;
455 embedder_api->UpdateLocales = MOCK_ENGINE_PROC(
456 UpdateLocales, ([&called](auto engine, const FlutterLocale** locales,
457 size_t locales_count) {
458 called = true;
459
460 EXPECT_EQ(locales_count, static_cast<size_t>(2));
461
462 EXPECT_STREQ(locales[0]->language_code, "en");
463 EXPECT_STREQ(locales[0]->country_code, nullptr);
464 EXPECT_STREQ(locales[0]->script_code, nullptr);
465 EXPECT_STREQ(locales[0]->variant_code, nullptr);
466
467 EXPECT_STREQ(locales[1]->language_code, "C");
468 EXPECT_STREQ(locales[1]->country_code, nullptr);
469 EXPECT_STREQ(locales[1]->script_code, nullptr);
470 EXPECT_STREQ(locales[1]->variant_code, nullptr);
471
472 return kSuccess;
473 }));
474
475 g_autoptr(GError) error = nullptr;
476 EXPECT_TRUE(fl_engine_start(engine, &error));
477 EXPECT_EQ(error, nullptr);
478
479 EXPECT_TRUE(called);
480
481 if (initial_language) {
482 g_setenv("LANGUAGE", initial_language, TRUE);
483 } else {
484 g_unsetenv("LANGUAGE");
485 }
486 }
487
488 TEST(FlEngineTest, EmptyLocales) {
489 g_autofree gchar* initial_language = g_strdup(g_getenv("LANGUAGE"));
490 g_setenv("LANGUAGE", "de:: :en_US", TRUE);
491 g_autoptr(FlDartProject) project = fl_dart_project_new();
492
493 g_autoptr(FlEngine) engine = make_mock_engine_with_project(project);
494 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
495
496 bool called = false;
497 embedder_api->UpdateLocales = MOCK_ENGINE_PROC(
498 UpdateLocales, ([&called](auto engine, const FlutterLocale** locales,
499 size_t locales_count) {
500 called = true;
501
502 EXPECT_EQ(locales_count, static_cast<size_t>(4));
503
504 EXPECT_STREQ(locales[0]->language_code, "de");
505 EXPECT_STREQ(locales[0]->country_code, nullptr);
506 EXPECT_STREQ(locales[0]->script_code, nullptr);
507 EXPECT_STREQ(locales[0]->variant_code, nullptr);
508
509 EXPECT_STREQ(locales[1]->language_code, "en");
510 EXPECT_STREQ(locales[1]->country_code, "US");
511 EXPECT_STREQ(locales[1]->script_code, nullptr);
512 EXPECT_STREQ(locales[1]->variant_code, nullptr);
513
514 EXPECT_STREQ(locales[2]->language_code, "en");
515 EXPECT_STREQ(locales[2]->country_code, nullptr);
516 EXPECT_STREQ(locales[2]->script_code, nullptr);
517 EXPECT_STREQ(locales[2]->variant_code, nullptr);
518
519 EXPECT_STREQ(locales[3]->language_code, "C");
520 EXPECT_STREQ(locales[3]->country_code, nullptr);
521 EXPECT_STREQ(locales[3]->script_code, nullptr);
522 EXPECT_STREQ(locales[3]->variant_code, nullptr);
523
524 return kSuccess;
525 }));
526
527 g_autoptr(GError) error = nullptr;
528 EXPECT_TRUE(fl_engine_start(engine, &error));
529 EXPECT_EQ(error, nullptr);
530
531 EXPECT_TRUE(called);
532
533 if (initial_language) {
534 g_setenv("LANGUAGE", initial_language, TRUE);
535 } else {
536 g_unsetenv("LANGUAGE");
537 }
538 }
539
540 TEST(FlEngineTest, SwitchesEmpty) {
541 g_autoptr(FlEngine) engine = make_mock_engine();
542
543 // Clear the main environment variable, since test order is not guaranteed.
544 unsetenv("FLUTTER_ENGINE_SWITCHES");
545
546 g_autoptr(GPtrArray) switches = fl_engine_get_switches(engine);
547
548 EXPECT_EQ(switches->len, 0U);
549 }
550
551 static void add_view_cb(GObject* object,
552 GAsyncResult* result,
553 gpointer user_data) {
554 g_autoptr(GError) error = nullptr;
555 gboolean r = fl_engine_add_view_finish(FL_ENGINE(object), result, &error);
556 EXPECT_TRUE(r);
557 EXPECT_EQ(error, nullptr);
558
559 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
560 }
561
562 TEST(FlEngineTest, AddView) {
563 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
564
565 g_autoptr(FlEngine) engine = make_mock_engine();
566 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
567
568 bool called = false;
569 embedder_api->AddView = MOCK_ENGINE_PROC(
570 AddView, ([&called](auto engine, const FlutterAddViewInfo* info) {
571 called = true;
572 EXPECT_EQ(info->view_metrics->width, 123u);
573 EXPECT_EQ(info->view_metrics->height, 456u);
574 EXPECT_EQ(info->view_metrics->pixel_ratio, 2.0);
575
576 FlutterAddViewResult result;
577 result.struct_size = sizeof(FlutterAddViewResult);
578 result.added = true;
579 result.user_data = info->user_data;
580 info->add_view_callback(&result);
581
582 return kSuccess;
583 }));
584
585 FlutterViewId view_id =
586 fl_engine_add_view(engine, 123, 456, 2.0, nullptr, add_view_cb, loop);
587 EXPECT_GT(view_id, 0);
588 EXPECT_TRUE(called);
589
590 // Blocks here until add_view_cb is called.
591 g_main_loop_run(loop);
592 }
593
594 static void add_view_error_cb(GObject* object,
595 GAsyncResult* result,
596 gpointer user_data) {
597 g_autoptr(GError) error = nullptr;
598 gboolean r = fl_engine_add_view_finish(FL_ENGINE(object), result, &error);
599 EXPECT_FALSE(r);
600 EXPECT_NE(error, nullptr);
601
602 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
603 }
604
605 TEST(FlEngineTest, AddViewError) {
606 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
607
608 g_autoptr(FlEngine) engine = make_mock_engine();
609 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
610
611 embedder_api->AddView = MOCK_ENGINE_PROC(
612 AddView, ([](auto engine, const FlutterAddViewInfo* info) {
613 FlutterAddViewResult result;
614 result.struct_size = sizeof(FlutterAddViewResult);
615 result.added = false;
616 result.user_data = info->user_data;
617 info->add_view_callback(&result);
618
619 return kSuccess;
620 }));
621
622 FlutterViewId view_id = fl_engine_add_view(engine, 123, 456, 2.0, nullptr,
623 add_view_error_cb, loop);
624 EXPECT_GT(view_id, 0);
625
626 // Blocks here until add_view_error_cb is called.
627 g_main_loop_run(loop);
628 }
629
630 static void add_view_engine_error_cb(GObject* object,
631 GAsyncResult* result,
632 gpointer user_data) {
633 g_autoptr(GError) error = nullptr;
634 gboolean r = fl_engine_add_view_finish(FL_ENGINE(object), result, &error);
635 EXPECT_FALSE(r);
636 EXPECT_NE(error, nullptr);
637
638 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
639 }
640
641 TEST(FlEngineTest, AddViewEngineError) {
642 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
643
644 g_autoptr(FlEngine) engine = make_mock_engine();
645 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
646
647 embedder_api->AddView = MOCK_ENGINE_PROC(
648 AddView, ([](auto engine, const FlutterAddViewInfo* info) {
649 return kInvalidArguments;
650 }));
651
652 FlutterViewId view_id = fl_engine_add_view(engine, 123, 456, 2.0, nullptr,
653 add_view_engine_error_cb, loop);
654 EXPECT_GT(view_id, 0);
655
656 // Blocks here until remove_view_engine_error_cb is called.
657 g_main_loop_run(loop);
658 }
659
660 static void remove_view_cb(GObject* object,
661 GAsyncResult* result,
662 gpointer user_data) {
663 g_autoptr(GError) error = nullptr;
664 gboolean r = fl_engine_remove_view_finish(FL_ENGINE(object), result, &error);
665 EXPECT_TRUE(r);
666 EXPECT_EQ(error, nullptr);
667
668 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
669 }
670
671 TEST(FlEngineTest, RemoveView) {
672 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
673
674 g_autoptr(FlEngine) engine = make_mock_engine();
675 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
676
677 bool called = false;
678 embedder_api->RemoveView = MOCK_ENGINE_PROC(
679 RemoveView, ([&called](auto engine, const FlutterRemoveViewInfo* info) {
680 called = true;
681 EXPECT_EQ(info->view_id, 123);
682
683 FlutterRemoveViewResult result;
684 result.struct_size = sizeof(FlutterRemoveViewResult);
685 result.removed = true;
686 result.user_data = info->user_data;
687 info->remove_view_callback(&result);
688
689 return kSuccess;
690 }));
691
692 fl_engine_remove_view(engine, 123, nullptr, remove_view_cb, loop);
693 EXPECT_TRUE(called);
694
695 // Blocks here until remove_view_cb is called.
696 g_main_loop_run(loop);
697 }
698
699 static void remove_view_error_cb(GObject* object,
700 GAsyncResult* result,
701 gpointer user_data) {
702 g_autoptr(GError) error = nullptr;
703 gboolean r = fl_engine_remove_view_finish(FL_ENGINE(object), result, &error);
704 EXPECT_FALSE(r);
705 EXPECT_NE(error, nullptr);
706
707 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
708 }
709
710 TEST(FlEngineTest, RemoveViewError) {
711 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
712
713 g_autoptr(FlEngine) engine = make_mock_engine();
714 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
715
716 embedder_api->RemoveView = MOCK_ENGINE_PROC(
717 RemoveView, ([](auto engine, const FlutterRemoveViewInfo* info) {
718 FlutterRemoveViewResult result;
719 result.struct_size = sizeof(FlutterRemoveViewResult);
720 result.removed = false;
721 result.user_data = info->user_data;
722 info->remove_view_callback(&result);
723
724 return kSuccess;
725 }));
726
727 fl_engine_remove_view(engine, 123, nullptr, remove_view_error_cb, loop);
728
729 // Blocks here until remove_view_error_cb is called.
730 g_main_loop_run(loop);
731 }
732
733 static void remove_view_engine_error_cb(GObject* object,
734 GAsyncResult* result,
735 gpointer user_data) {
736 g_autoptr(GError) error = nullptr;
737 gboolean r = fl_engine_remove_view_finish(FL_ENGINE(object), result, &error);
738 EXPECT_FALSE(r);
739 EXPECT_NE(error, nullptr);
740
741 g_main_loop_quit(static_cast<GMainLoop*>(user_data));
742 }
743
744 TEST(FlEngineTest, RemoveViewEngineError) {
745 g_autoptr(GMainLoop) loop = g_main_loop_new(nullptr, 0);
746
747 g_autoptr(FlEngine) engine = make_mock_engine();
748 FlutterEngineProcTable* embedder_api = fl_engine_get_embedder_api(engine);
749
750 embedder_api->RemoveView = MOCK_ENGINE_PROC(
751 RemoveView, ([](auto engine, const FlutterRemoveViewInfo* info) {
752 return kInvalidArguments;
753 }));
754
755 fl_engine_remove_view(engine, 123, nullptr, remove_view_engine_error_cb,
756 loop);
757
758 // Blocks here until remove_view_engine_error_cb is called.
759 g_main_loop_run(loop);
760 }
761
762 #ifndef FLUTTER_RELEASE
763 TEST(FlEngineTest, Switches) {
764 g_autoptr(FlEngine) engine = make_mock_engine();
765
766 setenv("FLUTTER_ENGINE_SWITCHES", "2", 1);
767 setenv("FLUTTER_ENGINE_SWITCH_1", "abc", 1);
768 setenv("FLUTTER_ENGINE_SWITCH_2", "foo=\"bar, baz\"", 1);
769
770 g_autoptr(GPtrArray) switches = fl_engine_get_switches(engine);
771 EXPECT_EQ(switches->len, 2U);
772 EXPECT_STREQ(static_cast<const char*>(g_ptr_array_index(switches, 0)),
773 "--abc");
774 EXPECT_STREQ(static_cast<const char*>(g_ptr_array_index(switches, 1)),
775 "--foo=\"bar, baz\"");
776
777 unsetenv("FLUTTER_ENGINE_SWITCHES");
778 unsetenv("FLUTTER_ENGINE_SWITCH_1");
779 unsetenv("FLUTTER_ENGINE_SWITCH_2");
780 }
781 #endif // !FLUTTER_RELEASE
782
783 // NOLINTEND(clang-analyzer-core.StackAddressEscape)
event
FlKeyEvent * event
Definition: fl_key_channel_responder.cc:118
fl_engine_get_embedder_api
FlutterEngineProcTable * fl_engine_get_embedder_api(FlEngine *self)
Definition: fl_engine.cc:637
add_view_engine_error_cb
static void add_view_engine_error_cb(GObject *object, GAsyncResult *result, gpointer user_data)
Definition: fl_engine_test.cc:630
fl_string_codec.h
FlValue
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition: fl_value.h:42
fl_json_message_codec_new
G_MODULE_EXPORT FlJsonMessageCodec * fl_json_message_codec_new()
Definition: fl_json_message_codec.cc:306
fl_json_message_codec.h
fl_engine_remove_view
void fl_engine_remove_view(FlEngine *self, FlutterViewId view_id, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Definition: fl_engine.cc:686
user_data
FlKeyEvent uint64_t FlKeyResponderAsyncCallback gpointer user_data
Definition: fl_key_channel_responder.cc:121
fl_engine_dispatch_semantics_action
void fl_engine_dispatch_semantics_action(FlEngine *self, uint64_t id, FlutterSemanticsAction action, GBytes *data)
Definition: fl_engine.cc:953
fl_dart_project_new
G_MODULE_EXPORT FlDartProject * fl_dart_project_new()
Definition: fl_dart_project.cc:50
fl_value_lookup_string
G_MODULE_EXPORT FlValue * fl_value_lookup_string(FlValue *self, const gchar *key)
Definition: fl_value.cc:811
fl_engine_send_window_metrics_event
void fl_engine_send_window_metrics_event(FlEngine *self, FlutterViewId view_id, size_t width, size_t height, double pixel_ratio)
Definition: fl_engine.cc:854
make_mock_engine
static FlEngine * make_mock_engine()
Definition: fl_event_channel_test.cc:24
fl_message_codec_decode_message
G_MODULE_EXPORT FlValue * fl_message_codec_decode_message(FlMessageCodec *self, GBytes *message, GError **error)
Definition: fl_message_codec.cc:33
fl_value_get_type
G_MODULE_EXPORT FlValueType fl_value_get_type(FlValue *self)
Definition: fl_value.cc:466
fl_engine_add_view_finish
gboolean fl_engine_add_view_finish(FlEngine *self, GAsyncResult *result, GError **error)
Definition: fl_engine.cc:679
fl_engine_remove_view_finish
gboolean fl_engine_remove_view_finish(FlEngine *self, GAsyncResult *result, GError **error)
Definition: fl_engine.cc:711
FL_VALUE_TYPE_STRING
@ FL_VALUE_TYPE_STRING
Definition: fl_value.h:69
fl_dart_project_set_dart_entrypoint_arguments
G_MODULE_EXPORT void fl_dart_project_set_dart_entrypoint_arguments(FlDartProject *self, char **argv)
Definition: fl_dart_project.cc:110
fl_engine_private.h
remove_view_engine_error_cb
static void remove_view_engine_error_cb(GObject *object, GAsyncResult *result, gpointer user_data)
Definition: fl_engine_test.cc:733
TRUE
return TRUE
Definition: fl_pixel_buffer_texture_test.cc:53
TEST
TEST(FlEngineTest, WindowMetrics)
Definition: fl_engine_test.cc:19
fl_engine_add_view
FlutterViewId fl_engine_add_view(FlEngine *self, size_t width, size_t height, double pixel_ratio, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Definition: fl_engine.cc:641
add_view_error_cb
static void add_view_error_cb(GObject *object, GAsyncResult *result, gpointer user_data)
Definition: fl_engine_test.cc:594
add_view_cb
static void add_view_cb(GObject *object, GAsyncResult *result, gpointer user_data)
Definition: fl_engine_test.cc:551
remove_view_cb
static void remove_view_cb(GObject *object, GAsyncResult *result, gpointer user_data)
Definition: fl_engine_test.cc:660
result
GAsyncResult * result
Definition: fl_text_input_handler.cc:106
args
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
Definition: fl_event_channel.h:89
fl_engine.h
error
const uint8_t uint32_t uint32_t GError ** error
Definition: fl_pixel_buffer_texture_test.cc:40
fl_engine_send_pointer_pan_zoom_event
void fl_engine_send_pointer_pan_zoom_event(FlEngine *self, FlutterViewId view_id, size_t timestamp, double x, double y, FlutterPointerPhase phase, double pan_x, double pan_y, double scale, double rotation)
Definition: fl_engine.cc:908
fl_engine_send_platform_message
void fl_engine_send_platform_message(FlEngine *self, const gchar *channel, GBytes *message, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Definition: fl_engine.cc:786
on_pre_engine_restart_cb
void on_pre_engine_restart_cb(FlEngine *engine, gpointer user_data)
Definition: fl_engine_test.cc:273
fl_engine_start
gboolean fl_engine_start(FlEngine *self, GError **error)
Definition: fl_engine.cc:509
FL_VALUE_TYPE_FLOAT
@ FL_VALUE_TYPE_FLOAT
Definition: fl_value.h:68
callback
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
Definition: fl_key_channel_responder.cc:120
remove_view_error_cb
static void remove_view_error_cb(GObject *object, GAsyncResult *result, gpointer user_data)
Definition: fl_engine_test.cc:699
fl_engine_send_mouse_pointer_event
void fl_engine_send_mouse_pointer_event(FlEngine *self, FlutterViewId view_id, 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:874
fl_engine_get_switches
GPtrArray * fl_engine_get_switches(FlEngine *self)
Definition: fl_engine.cc:1028
FL_VALUE_TYPE_BOOL
@ FL_VALUE_TYPE_BOOL
Definition: fl_value.h:66
fl_engine_send_platform_message_response
gboolean fl_engine_send_platform_message_response(FlEngine *self, const FlutterPlatformMessageResponseHandle *handle, GBytes *response, GError **error)
Definition: fl_engine.cc:754