Flutter Linux Embedder
fl_view_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 
6 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
9 #include "flutter/shell/platform/linux/testing/fl_test.h"
10 #include "flutter/shell/platform/linux/testing/fl_test_gtk_logs.h"
11 #include "flutter/shell/platform/linux/testing/mock_gtk.h"
12 
13 #include "gtest/gtest.h"
14 
15 // FIXME(robert-ancell): Disabled, see below.
16 #if 0
17 static void first_frame_cb(FlView* view, gboolean* first_frame_emitted) {
18  *first_frame_emitted = TRUE;
19 }
20 #endif
21 
22 TEST(FlViewTest, GetEngine) {
23  flutter::testing::fl_ensure_gtk_init();
24  g_autoptr(FlDartProject) project = fl_dart_project_new();
25  FlView* view = fl_view_new(project);
26 
27  // Check the engine is immediately available (i.e. before the widget is
28  // realized).
29  FlEngine* engine = fl_view_get_engine(view);
30  EXPECT_NE(engine, nullptr);
31 }
32 
33 TEST(FlViewTest, StateUpdateDoesNotHappenInInit) {
34  flutter::testing::fl_ensure_gtk_init();
35  g_autoptr(FlDartProject) project = fl_dart_project_new();
36  FlView* view = fl_view_new(project);
37  // Check that creating a view doesn't try to query the window state in
38  // initialization, causing a critical log to be issued.
39  EXPECT_EQ(
40  flutter::testing::fl_get_received_gtk_log_levels() & G_LOG_LEVEL_CRITICAL,
41  (GLogLevelFlags)0x0);
42 
43  (void)view;
44 }
45 
46 // FIXME(robert-ancell): Disabling this test as it requires the FlView
47 // to be realized to work after some refactoring. This is proving to be
48 // very difficult to mock. Following PRs will change this code so enable the
49 // test again after that.
50 #if 0
51 TEST(FlViewTest, FirstFrameSignal) {
52  flutter::testing::fl_ensure_gtk_init();
53 
54  g_autoptr(FlDartProject) project = fl_dart_project_new();
55  FlView* view = fl_view_new(project);
56  gboolean first_frame_emitted = FALSE;
57  g_signal_connect(view, "first-frame", G_CALLBACK(first_frame_cb),
58  &first_frame_emitted);
59 
60  EXPECT_FALSE(first_frame_emitted);
61 
62  fl_renderable_present_layers(FL_RENDERABLE(view), nullptr, 0);
63 
64  // Signal is emitted in idle, clear the main loop.
65  while (g_main_context_iteration(g_main_context_default(), FALSE)) {
66  // Repeat until nothing to iterate on.
67  }
68 
69  // Check view has detected frame.
70  EXPECT_TRUE(first_frame_emitted);
71 }
72 #endif
73 
74 // Check semantics update applied
75 TEST(FlViewTest, SemanticsUpdate) {
76  flutter::testing::fl_ensure_gtk_init();
77 
78  g_autoptr(FlDartProject) project = fl_dart_project_new();
79  FlView* view = fl_view_new(project);
80 
81  FlEngine* engine = fl_view_get_engine(view);
82  g_autoptr(GError) error = nullptr;
83  EXPECT_TRUE(fl_engine_start(engine, &error));
84 
85  FlutterSemanticsFlags flags = {};
86  FlutterSemanticsNode2 root_node = {
87  .id = 0, .label = "root", .flags2 = &flags};
88  FlutterSemanticsNode2* nodes[1] = {&root_node};
89  FlutterSemanticsUpdate2 update = {
90  .node_count = 1, .nodes = nodes, .view_id = 0};
91  g_signal_emit_by_name(engine, "update-semantics", &update);
92 
93  FlViewAccessible* accessible = fl_view_get_accessible(view);
94  EXPECT_EQ(atk_object_get_n_accessible_children(ATK_OBJECT(accessible)), 1);
95  AtkObject* root_object =
96  atk_object_ref_accessible_child(ATK_OBJECT(accessible), 0);
97  EXPECT_STREQ(atk_object_get_name(root_object), "root");
98 }
99 
100 // Check semantics update ignored for other view
101 TEST(FlViewTest, SemanticsUpdateOtherView) {
102  flutter::testing::fl_ensure_gtk_init();
103 
104  g_autoptr(FlDartProject) project = fl_dart_project_new();
105  FlView* view = fl_view_new(project);
106 
107  FlEngine* engine = fl_view_get_engine(view);
108  g_autoptr(GError) error = nullptr;
109  EXPECT_TRUE(fl_engine_start(engine, &error));
110 
111  FlutterSemanticsFlags flags = {};
112  FlutterSemanticsNode2 root_node = {
113  .id = 0, .label = "root", .flags2 = &flags};
114  FlutterSemanticsNode2* nodes[1] = {&root_node};
115  FlutterSemanticsUpdate2 update = {
116  .node_count = 1, .nodes = nodes, .view_id = 99};
117  g_signal_emit_by_name(engine, "update-semantics", &update);
118 
119  FlViewAccessible* accessible = fl_view_get_accessible(view);
120  EXPECT_EQ(atk_object_get_n_accessible_children(ATK_OBJECT(accessible)), 0);
121 }
122 
123 // Check secondary view is registered with engine.
124 TEST(FlViewTest, SecondaryView) {
125  flutter::testing::fl_ensure_gtk_init();
126 
127  g_autoptr(FlDartProject) project = fl_dart_project_new();
128  FlView* implicit_view = fl_view_new(project);
129 
130  FlEngine* engine = fl_view_get_engine(implicit_view);
131 
132  FlutterViewId view_id = -1;
133  fl_engine_get_embedder_api(engine)->AddView = MOCK_ENGINE_PROC(
134  AddView, ([&view_id](auto engine, const FlutterAddViewInfo* info) {
135  view_id = info->view_id;
136  FlutterAddViewResult result = {
137  .struct_size = sizeof(FlutterAddViewResult),
138  .added = true,
139  .user_data = info->user_data};
140  info->add_view_callback(&result);
141  return kSuccess;
142  }));
143 
144  g_autoptr(GError) error = nullptr;
145  EXPECT_TRUE(fl_engine_start(engine, &error));
146 
147  FlView* secondary_view = fl_view_new_for_engine(engine);
148  EXPECT_EQ(view_id, fl_view_get_id(secondary_view));
149 }
150 
151 // Check secondary view that fails registration.
152 TEST(FlViewTest, SecondaryViewError) {
153  flutter::testing::fl_ensure_gtk_init();
154 
155  g_autoptr(FlDartProject) project = fl_dart_project_new();
156  FlView* implicit_view = fl_view_new(project);
157 
158  FlEngine* engine = fl_view_get_engine(implicit_view);
159 
160  FlutterViewId view_id = -1;
161  fl_engine_get_embedder_api(engine)->AddView = MOCK_ENGINE_PROC(
162  AddView, ([&view_id](auto engine, const FlutterAddViewInfo* info) {
163  view_id = info->view_id;
164  return kInvalidArguments;
165  }));
166 
167  g_autoptr(GError) error = nullptr;
168  EXPECT_TRUE(fl_engine_start(engine, &error));
169 
170  FlView* secondary_view = fl_view_new_for_engine(engine);
171  EXPECT_EQ(view_id, fl_view_get_id(secondary_view));
172 }
173 
174 // Check views are deregistered on destruction.
175 TEST(FlViewTest, ViewDestroy) {
176  flutter::testing::fl_ensure_gtk_init();
177 
178  g_autoptr(FlDartProject) project = fl_dart_project_new();
179  FlView* implicit_view = fl_view_new(project);
180 
181  FlEngine* engine = fl_view_get_engine(implicit_view);
182 
183  g_autoptr(GPtrArray) removed_views = g_ptr_array_new();
184  fl_engine_get_embedder_api(engine)->RemoveView = MOCK_ENGINE_PROC(
185  RemoveView,
186  ([removed_views](auto engine, const FlutterRemoveViewInfo* info) {
187  g_ptr_array_add(removed_views, GINT_TO_POINTER(info->view_id));
188  return kSuccess;
189  }));
190 
191  g_autoptr(GError) error = nullptr;
192  EXPECT_TRUE(fl_engine_start(engine, &error));
193 
194  FlView* secondary_view = fl_view_new_for_engine(engine);
195 
196  int64_t implicit_view_id = fl_view_get_id(implicit_view);
197  int64_t secondary_view_id = fl_view_get_id(secondary_view);
198 
199  fl_gtk_widget_destroy(GTK_WIDGET(secondary_view));
200  fl_gtk_widget_destroy(GTK_WIDGET(implicit_view));
201 
202  EXPECT_EQ(removed_views->len, 2u);
203  EXPECT_EQ(GPOINTER_TO_INT(g_ptr_array_index(removed_views, 0)),
204  secondary_view_id);
205  EXPECT_EQ(GPOINTER_TO_INT(g_ptr_array_index(removed_views, 1)),
206  implicit_view_id);
207 }
208 
209 // Check views deregistered with errors works.
210 TEST(FlViewTest, ViewDestroyError) {
211  flutter::testing::fl_ensure_gtk_init();
212 
213  g_autoptr(FlDartProject) project = fl_dart_project_new();
214  FlView* implicit_view = fl_view_new(project);
215 
216  FlEngine* engine = fl_view_get_engine(implicit_view);
217 
218  fl_engine_get_embedder_api(engine)->RemoveView = MOCK_ENGINE_PROC(
219  RemoveView, ([](auto engine, const FlutterRemoveViewInfo* info) {
220  return kInvalidArguments;
221  }));
222 
223  g_autoptr(GError) error = nullptr;
224  EXPECT_TRUE(fl_engine_start(engine, &error));
225 
226  FlView* secondary_view = fl_view_new_for_engine(engine);
227 
228  fl_gtk_widget_destroy(GTK_WIDGET(secondary_view));
229  fl_gtk_widget_destroy(GTK_WIDGET(implicit_view));
230 }
FlView * view
static void first_frame_cb(FlApplication *self, FlView *view)
G_MODULE_EXPORT FlDartProject * fl_dart_project_new()
FlutterEngineProcTable * fl_engine_get_embedder_api(FlEngine *self)
Definition: fl_engine.cc:858
gboolean fl_engine_start(FlEngine *self, GError **error)
Definition: fl_engine.cc:720
G_BEGIN_DECLS G_MODULE_EXPORT FlValue gpointer user_data
const uint8_t uint32_t uint32_t GError ** error
void fl_renderable_present_layers(FlRenderable *self, const FlutterLayer **layers, size_t layers_count)
FlViewAccessible * fl_view_get_accessible(FlView *self)
Definition: fl_view.cc:856
G_MODULE_EXPORT FlView * fl_view_new(FlDartProject *project)
Definition: fl_view.cc:811
G_MODULE_EXPORT FlView * fl_view_new_for_engine(FlEngine *engine)
Definition: fl_view.cc:825
G_MODULE_EXPORT FlEngine * fl_view_get_engine(FlView *self)
Definition: fl_view.cc:838
G_MODULE_EXPORT int64_t fl_view_get_id(FlView *self)
Definition: fl_view.cc:844
G_BEGIN_DECLS FlutterViewId view_id
TEST(FlViewTest, GetEngine)
Definition: fl_view_test.cc:22