| Flutter Windows Embedder |

flutter_window_unittests.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/fml/macros.h"
6 #include "flutter/shell/platform/windows/flutter_window.h"
7 #include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h"
8 #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
9 #include "flutter/shell/platform/windows/testing/mock_window_binding_handler_delegate.h"
10 #include "flutter/shell/platform/windows/testing/windows_test.h"
11 #include "flutter/shell/platform/windows/testing/wm_builders.h"
12
13 #include "gmock/gmock.h"
14 #include "gtest/gtest.h"
15
16 namespace flutter {
17 namespace testing {
18
19 using ::testing::_;
20 using ::testing::AnyNumber;
21 using ::testing::Eq;
22 using ::testing::Invoke;
23 using ::testing::Return;
24
25 namespace {
26 static constexpr int32_t kDefaultPointerDeviceId = 0;
27
28 class MockFlutterWindow : public FlutterWindow {
29 public:
30 MockFlutterWindow(bool reset_view_on_exit = true)
31 : reset_view_on_exit_(reset_view_on_exit) {
32 ON_CALL(*this, GetDpiScale())
33 .WillByDefault(Return(this->FlutterWindow::GetDpiScale()));
34 }
35 virtual ~MockFlutterWindow() {
36 if (reset_view_on_exit_) {
37 SetView(nullptr);
38 }
39 }
40
41 // Wrapper for GetCurrentDPI() which is a protected method.
42 UINT GetDpi() { return GetCurrentDPI(); }
43
44 // Simulates a WindowProc message from the OS.
45 LRESULT InjectWindowMessage(UINT const message,
46 WPARAM const wparam,
47 LPARAM const lparam) {
48 return HandleMessage(message, wparam, lparam);
49 }
50
51 MOCK_METHOD(void, OnDpiScale, (unsigned int), (override));
52 MOCK_METHOD(void, OnResize, (unsigned int, unsigned int), (override));
53 MOCK_METHOD(void,
54 OnPointerMove,
55 (double, double, FlutterPointerDeviceKind, int32_t, int),
56 (override));
57 MOCK_METHOD(void,
58 OnPointerDown,
59 (double, double, FlutterPointerDeviceKind, int32_t, UINT),
60 (override));
61 MOCK_METHOD(void,
62 OnPointerUp,
63 (double, double, FlutterPointerDeviceKind, int32_t, UINT),
64 (override));
65 MOCK_METHOD(void,
66 OnPointerLeave,
67 (double, double, FlutterPointerDeviceKind, int32_t),
68 (override));
69 MOCK_METHOD(float, GetScrollOffsetMultiplier, (), (override));
70 MOCK_METHOD(float, GetDpiScale, (), (override));
71 MOCK_METHOD(void, UpdateCursorRect, (const Rect&), (override));
72 MOCK_METHOD(void, OnResetImeComposing, (), (override));
73 MOCK_METHOD(UINT, Win32DispatchMessage, (UINT, WPARAM, LPARAM), (override));
74 MOCK_METHOD(BOOL, Win32PeekMessage, (LPMSG, UINT, UINT, UINT), (override));
75 MOCK_METHOD(uint32_t, Win32MapVkToChar, (uint32_t), (override));
76 MOCK_METHOD(HWND, GetWindowHandle, (), (override));
77 MOCK_METHOD(ui::AXFragmentRootDelegateWin*,
78 GetAxFragmentRootDelegate,
79 (),
80 (override));
81 MOCK_METHOD(void, OnWindowStateEvent, (WindowStateEvent), (override));
82
83 protected:
84 // |KeyboardManager::WindowDelegate|
85 LRESULT Win32DefWindowProc(HWND hWnd,
86 UINT Msg,
87 WPARAM wParam,
88 LPARAM lParam) override {
89 return kWmResultDefault;
90 }
91
92 private:
93 bool reset_view_on_exit_;
94 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindow);
95 };
96
97 class MockFlutterWindowsView : public FlutterWindowsView {
98 public:
99 MockFlutterWindowsView(FlutterWindowsEngine* engine,
100 std::unique_ptr<WindowBindingHandler> window_binding)
101 : FlutterWindowsView(kImplicitViewId,
102 engine,
103 std::move(window_binding),
104 false,
105 BoxConstraints()) {}
106 ~MockFlutterWindowsView() {}
107
108 MOCK_METHOD(void,
109 NotifyWinEventWrapper,
110 (ui::AXPlatformNodeWin*, ax::mojom::Event),
111 (override));
112
113 private:
114 FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
115 };
116
117 class FlutterWindowTest : public WindowsTest {};
118
119 } // namespace
120
121 TEST_F(FlutterWindowTest, CreateDestroy) {
122 std::unique_ptr<FlutterWindowsEngine> engine =
123 FlutterWindowsEngineBuilder{GetContext()}.Build();
124 FlutterWindow window(800, 600, engine->display_manager());
125 ASSERT_TRUE(TRUE);
126 }
127
128 TEST_F(FlutterWindowTest, OnBitmapSurfaceUpdated) {
129 std::unique_ptr<FlutterWindowsEngine> engine =
130 FlutterWindowsEngineBuilder{GetContext()}.Build();
131 FlutterWindow win32window(100, 100, engine->display_manager());
132 int old_handle_count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
133
134 constexpr size_t row_bytes = 100 * 4;
135 constexpr size_t height = 100;
136 std::array<char, row_bytes * height> allocation;
137 win32window.OnBitmapSurfaceUpdated(allocation.data(), row_bytes, height);
138
139 int new_handle_count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
140 // Check GDI resources leak
141 EXPECT_EQ(old_handle_count, new_handle_count);
142 }
143
144 // Tests that composing rect updates are transformed from Flutter logical
145 // coordinates to device coordinates and passed to the text input manager
146 // when the DPI scale is 100% (96 DPI).
147 TEST_F(FlutterWindowTest, OnCursorRectUpdatedRegularDPI) {
148 MockFlutterWindow win32window;
149 EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.0));
150
151 Rect cursor_rect(Point(10, 20), Size(30, 40));
152 EXPECT_CALL(win32window, UpdateCursorRect(cursor_rect)).Times(1);
153
154 win32window.OnCursorRectUpdated(cursor_rect);
155 }
156
157 // Tests that composing rect updates are transformed from Flutter logical
158 // coordinates to device coordinates and passed to the text input manager
159 // when the DPI scale is 150% (144 DPI).
160 TEST_F(FlutterWindowTest, OnCursorRectUpdatedHighDPI) {
161 MockFlutterWindow win32window;
162 EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.5));
163
164 Rect expected_cursor_rect(Point(15, 30), Size(45, 60));
165 EXPECT_CALL(win32window, UpdateCursorRect(expected_cursor_rect)).Times(1);
166
167 Rect cursor_rect(Point(10, 20), Size(30, 40));
168 win32window.OnCursorRectUpdated(cursor_rect);
169 }
170
171 TEST_F(FlutterWindowTest, OnPointerStarSendsDeviceType) {
172 std::unique_ptr<FlutterWindowsEngine> engine =
173 FlutterWindowsEngineBuilder{GetContext()}.Build();
174 FlutterWindow win32window(100, 100, engine->display_manager());
175 MockWindowBindingHandlerDelegate delegate;
176 EXPECT_CALL(delegate, OnWindowStateEvent).Times(AnyNumber());
177 win32window.SetView(&delegate);
178
179 // Move
180 EXPECT_CALL(delegate,
181 OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
182 kDefaultPointerDeviceId, 0))
183 .Times(1);
184 EXPECT_CALL(delegate,
185 OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
186 kDefaultPointerDeviceId, 0))
187 .Times(1);
188 EXPECT_CALL(delegate,
189 OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
190 kDefaultPointerDeviceId, 0))
191 .Times(1);
192
193 // Down
194 EXPECT_CALL(
195 delegate,
196 OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
197 kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
198 .Times(1);
199 EXPECT_CALL(
200 delegate,
201 OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
202 kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
203 .Times(1);
204 EXPECT_CALL(
205 delegate,
206 OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
207 kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
208 .Times(1);
209
210 // Up
211 EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
212 kDefaultPointerDeviceId,
213 kFlutterPointerButtonMousePrimary))
214 .Times(1);
215 EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
216 kDefaultPointerDeviceId,
217 kFlutterPointerButtonMousePrimary))
218 .Times(1);
219 EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
220 kDefaultPointerDeviceId,
221 kFlutterPointerButtonMousePrimary))
222 .Times(1);
223
224 // Leave
225 EXPECT_CALL(delegate,
226 OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindMouse,
227 kDefaultPointerDeviceId))
228 .Times(1);
229 EXPECT_CALL(delegate,
230 OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindTouch,
231 kDefaultPointerDeviceId))
232 .Times(1);
233 EXPECT_CALL(delegate,
234 OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
235 kDefaultPointerDeviceId))
236 .Times(1);
237
238 win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
239 kDefaultPointerDeviceId, 0);
240 win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
241 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
242 win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
243 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
244 win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindMouse,
245 kDefaultPointerDeviceId);
246
247 // Touch
248 win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
249 kDefaultPointerDeviceId, 0);
250 win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
251 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
252 win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
253 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
254 win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindTouch,
255 kDefaultPointerDeviceId);
256
257 // Pen
258 win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
259 kDefaultPointerDeviceId, 0);
260 win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
261 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
262 win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
263 kDefaultPointerDeviceId, WM_LBUTTONDOWN);
264 win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
265 kDefaultPointerDeviceId);
266
267 // Destruction of win32window sends a HIDE update. In situ, the window is
268 // owned by the delegate, and so is destructed first. Not so here.
269 win32window.SetView(nullptr);
270 }
271
272 // Tests that calls to OnScroll in turn calls GetScrollOffsetMultiplier
273 // for mapping scroll ticks to pixels.
274 TEST_F(FlutterWindowTest, OnScrollCallsGetScrollOffsetMultiplier) {
275 MockFlutterWindow win32window;
276 MockWindowBindingHandlerDelegate delegate;
277 EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
278 win32window.SetView(&delegate);
279
280 EXPECT_CALL(win32window, GetWindowHandle).WillOnce([&win32window]() {
281 return win32window.FlutterWindow::GetWindowHandle();
282 });
283 EXPECT_CALL(win32window, GetScrollOffsetMultiplier).WillOnce(Return(120.0f));
284
285 EXPECT_CALL(delegate,
286 OnScroll(_, _, 0, 0, 120.0f, kFlutterPointerDeviceKindMouse,
287 kDefaultPointerDeviceId))
288 .Times(1);
289
290 win32window.OnScroll(0.0f, 0.0f, kFlutterPointerDeviceKindMouse,
291 kDefaultPointerDeviceId);
292 }
293
294 TEST_F(FlutterWindowTest, OnWindowRepaint) {
295 MockFlutterWindow win32window;
296 MockWindowBindingHandlerDelegate delegate;
297 EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
298 win32window.SetView(&delegate);
299
300 EXPECT_CALL(delegate, OnWindowRepaint()).Times(1);
301
302 win32window.InjectWindowMessage(WM_PAINT, 0, 0);
303 }
304
305 TEST_F(FlutterWindowTest, OnThemeChange) {
306 MockFlutterWindow win32window;
307 MockWindowBindingHandlerDelegate delegate;
308 EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
309 win32window.SetView(&delegate);
310
311 EXPECT_CALL(delegate, OnHighContrastChanged).Times(1);
312
313 win32window.InjectWindowMessage(WM_THEMECHANGED, 0, 0);
314 }
315
316 // The window should return no root accessibility node if
317 // it isn't attached to a view.
318 // Regression test for https://github.com/flutter/flutter/issues/129791
319 TEST_F(FlutterWindowTest, AccessibilityNodeWithoutView) {
320 MockFlutterWindow win32window;
321
322 EXPECT_EQ(win32window.GetNativeViewAccessible(), nullptr);
323 }
324
325 // Ensure that announcing the alert propagates the message to the alert node.
326 // Different screen readers use different properties for alerts.
327 TEST_F(FlutterWindowTest, AlertNode) {
328 std::unique_ptr<FlutterWindowsEngine> engine =
329 FlutterWindowsEngineBuilder{GetContext()}.Build();
330 auto win32window = std::make_unique<MockFlutterWindow>();
331 EXPECT_CALL(*win32window.get(), GetAxFragmentRootDelegate())
332 .WillRepeatedly(Return(nullptr));
333 EXPECT_CALL(*win32window.get(), OnWindowStateEvent).Times(AnyNumber());
334 EXPECT_CALL(*win32window.get(), GetWindowHandle).Times(AnyNumber());
335 MockFlutterWindowsView view{engine.get(), std::move(win32window)};
336 std::wstring message = L"Test alert";
337 EXPECT_CALL(view, NotifyWinEventWrapper(_, ax::mojom::Event::kAlert))
338 .Times(1);
339 view.AnnounceAlert(message);
340
341 IAccessible* alert = view.AlertNode();
342 VARIANT self{.vt = VT_I4, .lVal = CHILDID_SELF};
343 BSTR strptr;
344 alert->get_accName(self, &strptr);
345 EXPECT_EQ(message, strptr);
346
347 alert->get_accDescription(self, &strptr);
348 EXPECT_EQ(message, strptr);
349
350 alert->get_accValue(self, &strptr);
351 EXPECT_EQ(message, strptr);
352
353 VARIANT role;
354 alert->get_accRole(self, &role);
355 EXPECT_EQ(role.vt, VT_I4);
356 EXPECT_EQ(role.lVal, ROLE_SYSTEM_ALERT);
357 }
358
359 TEST_F(FlutterWindowTest, LifecycleFocusMessages) {
360 MockFlutterWindow win32window;
361 EXPECT_CALL(win32window, GetWindowHandle)
362 .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
363 MockWindowBindingHandlerDelegate delegate;
364
365 WindowStateEvent last_event;
366 EXPECT_CALL(delegate, OnWindowStateEvent)
367 .WillRepeatedly([&last_event](HWND hwnd, WindowStateEvent event) {
368 last_event = event;
369 });
370 EXPECT_CALL(win32window, OnWindowStateEvent)
371 .WillRepeatedly([&](WindowStateEvent event) {
372 win32window.FlutterWindow::OnWindowStateEvent(event);
373 });
374 EXPECT_CALL(win32window, OnResize).Times(AnyNumber());
375
376 win32window.SetView(&delegate);
377
378 win32window.InjectWindowMessage(WM_SIZE, 0, 0);
379 EXPECT_EQ(last_event, WindowStateEvent::kHide);
380
381 win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
382 EXPECT_EQ(last_event, WindowStateEvent::kShow);
383
384 EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kFocused),
385 Eq(FlutterViewFocusDirection::kUndefined)))
386 .Times(1);
387 win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
388 EXPECT_EQ(last_event, WindowStateEvent::kFocus);
389
390 EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kUnfocused),
391 Eq(FlutterViewFocusDirection::kUndefined)))
392 .Times(1);
393 win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0);
394 EXPECT_EQ(last_event, WindowStateEvent::kUnfocus);
395 }
396
397 TEST_F(FlutterWindowTest, CachedLifecycleMessage) {
398 MockFlutterWindow win32window;
399 EXPECT_CALL(win32window, GetWindowHandle)
400 .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
401 EXPECT_CALL(win32window, OnWindowStateEvent)
402 .WillRepeatedly([&](WindowStateEvent event) {
403 win32window.FlutterWindow::OnWindowStateEvent(event);
404 });
405 EXPECT_CALL(win32window, OnResize).Times(1);
406
407 // Restore
408 win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
409
410 // Focus
411 win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
412
413 MockWindowBindingHandlerDelegate delegate;
414 bool focused = false;
415 bool restored = false;
416 EXPECT_CALL(delegate, OnWindowStateEvent)
417 .WillRepeatedly([&](HWND hwnd, WindowStateEvent event) {
418 if (event == WindowStateEvent::kFocus) {
419 focused = true;
420 } else if (event == WindowStateEvent::kShow) {
421 restored = true;
422 }
423 });
424
425 EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kFocused),
426 Eq(FlutterViewFocusDirection::kUndefined)))
427 .Times(1);
428 win32window.SetView(&delegate);
429 EXPECT_TRUE(focused);
430 EXPECT_TRUE(restored);
431 }
432
433 } // namespace testing
434 } // namespace flutter
flutter::FlutterWindow
Definition: flutter_window.h:38
flutter::FlutterWindow::OnPointerLeave
virtual void OnPointerLeave(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id)
Definition: flutter_window.cc:203
flutter::FlutterWindow::OnBitmapSurfaceUpdated
virtual bool OnBitmapSurfaceUpdated(const void *allocation, size_t row_bytes, size_t height) override
Definition: flutter_window.cc:278
flutter::FlutterWindow::OnPointerUp
virtual void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button)
Definition: flutter_window.cc:190
flutter::FlutterWindow::OnPointerDown
virtual void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button)
Definition: flutter_window.cc:177
flutter::FlutterWindow::OnPointerMove
virtual void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, int modifiers_state)
Definition: flutter_window.cc:168
flutter::FlutterWindow::SetView
virtual void SetView(WindowBindingHandlerDelegate *view) override
Definition: flutter_window.cc:117
flutter::FlutterWindow::GetDpiScale
virtual float GetDpiScale() override
Definition: flutter_window.cc:130
flutter::FlutterWindowsView::FlutterWindowsView
FlutterWindowsView(FlutterViewId view_id, FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > window_binding, bool is_sized_to_content, const BoxConstraints &box_constraints, FlutterWindowsViewSizingDelegate *sizing_delegate=nullptr, std::shared_ptr< WindowsProcTable > windows_proc_table=nullptr)
Definition: flutter_windows_view.cc:104
flutter::testing::MockFlutterWindowsView::MOCK_METHOD
MOCK_METHOD(void, NotifyWinEventWrapper,(ui::AXPlatformNodeWin *, ax::mojom::Event),(override))
flutter::testing::MockFlutterWindowsView::MockFlutterWindowsView
MockFlutterWindowsView(FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > wbh)
Definition: flutter_windows_engine_unittests.cc:770
flutter_window.h
message
Win32Message message
Definition: keyboard_unittests.cc:137
flutter::testing::TEST_F
TEST_F(AccessibilityPluginTest, DirectAnnounceCall)
Definition: accessibility_plugin_unittests.cc:102
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::WindowStateEvent
WindowStateEvent
An event representing a change in window state that may update the.
Definition: windows_lifecycle_manager.h:24
flutter::WindowStateEvent::kHide
@ kHide
flutter::WindowStateEvent::kUnfocus
@ kUnfocus
flutter::WindowStateEvent::kShow
@ kShow
flutter::WindowStateEvent::kFocus
@ kFocus
flutter::kImplicitViewId
constexpr FlutterViewId kImplicitViewId
Definition: flutter_windows_engine.h:56