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