| 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
5 #include "flutter/shell/platform/linux/public/flutter_linux/fl_view.h"
6 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
7 #include "flutter/shell/platform/linux/fl_engine_private.h"
8 #include "flutter/shell/platform/linux/fl_view_private.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 }
accessible
FlViewAccessible * accessible
Definition: fl_accessibility_handler.cc:41
view
FlView * view
Definition: fl_accessibility_handler.cc:40
g_autoptr
g_autoptr(FlEngine) engine
user_data
const char FlTextDirection FlAssertiveness gpointer user_data
Definition: fl_accessibility_handler.cc:24
TRUE
return TRUE
Definition: fl_compositor_software.cc:68
fl_dart_project_new
G_MODULE_EXPORT FlDartProject * fl_dart_project_new()
Definition: fl_dart_project.cc:52
fl_engine_get_embedder_api
FlutterEngineProcTable * fl_engine_get_embedder_api(FlEngine *self)
Definition: fl_engine.cc:868
fl_engine_start
gboolean fl_engine_start(FlEngine *self, GError **error)
Definition: fl_engine.cc:726
fl_engine_private.h
error
const uint8_t uint32_t uint32_t GError ** error
Definition: fl_pixel_buffer_texture_test.cc:40
fl_renderable_present_layers
void fl_renderable_present_layers(FlRenderable *self, const FlutterLayer **layers, size_t layers_count)
Definition: fl_renderable.cc:11
fl_view_get_accessible
FlViewAccessible * fl_view_get_accessible(FlView *self)
Definition: fl_view.cc:808
fl_view_new
G_MODULE_EXPORT FlView * fl_view_new(FlDartProject *project)
Definition: fl_view.cc:763
fl_view_new_for_engine
G_MODULE_EXPORT FlView * fl_view_new_for_engine(FlEngine *engine)
Definition: fl_view.cc:777
fl_view_get_engine
G_MODULE_EXPORT FlEngine * fl_view_get_engine(FlView *self)
Definition: fl_view.cc:790
fl_view_get_id
G_MODULE_EXPORT int64_t fl_view_get_id(FlView *self)
Definition: fl_view.cc:796
fl_view.h
view_id
G_BEGIN_DECLS FlutterViewId view_id
Definition: fl_view_accessible.h:49
fl_view_private.h
TEST
TEST(FlViewTest, GetEngine)
Definition: fl_view_test.cc:22