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