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"
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);
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),
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 
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,
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 
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