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::AnyNumber;
16 using testing::Invoke;
17 using testing::Return;
18 
19 namespace flutter {
20 namespace testing {
21 
22 namespace {
23 static constexpr int32_t kDefaultPointerDeviceId = 0;
24 
25 class MockFlutterWindow : public FlutterWindow {
26  public:
27  MockFlutterWindow(bool reset_view_on_exit = true)
28  : FlutterWindow(800, 600), reset_view_on_exit_(reset_view_on_exit) {
29  ON_CALL(*this, GetDpiScale())
30  .WillByDefault(Return(this->FlutterWindow::GetDpiScale()));
31  }
32  virtual ~MockFlutterWindow() {
33  if (reset_view_on_exit_) {
34  SetView(nullptr);
35  }
36  }
37 
38  // Wrapper for GetCurrentDPI() which is a protected method.
39  UINT GetDpi() { return GetCurrentDPI(); }
40 
41  // Simulates a WindowProc message from the OS.
42  LRESULT InjectWindowMessage(UINT const message,
43  WPARAM const wparam,
44  LPARAM const lparam) {
45  return HandleMessage(message, wparam, lparam);
46  }
47 
48  MOCK_METHOD(void, OnDpiScale, (unsigned int), (override));
49  MOCK_METHOD(void, OnResize, (unsigned int, unsigned int), (override));
50  MOCK_METHOD(void,
51  OnPointerMove,
52  (double, double, FlutterPointerDeviceKind, int32_t, int),
53  (override));
54  MOCK_METHOD(void,
55  OnPointerDown,
56  (double, double, FlutterPointerDeviceKind, int32_t, UINT),
57  (override));
58  MOCK_METHOD(void,
59  OnPointerUp,
60  (double, double, FlutterPointerDeviceKind, int32_t, UINT),
61  (override));
62  MOCK_METHOD(void,
63  OnPointerLeave,
64  (double, double, FlutterPointerDeviceKind, int32_t),
65  (override));
66  MOCK_METHOD(void, OnSetCursor, (), (override));
67  MOCK_METHOD(float, GetScrollOffsetMultiplier, (), (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, GetWindowHandle, (), (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  EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.0));
138 
139  Rect cursor_rect(Point(10, 20), Size(30, 40));
140  EXPECT_CALL(win32window, UpdateCursorRect(cursor_rect)).Times(1);
141 
142  win32window.OnCursorRectUpdated(cursor_rect);
143 }
144 
145 // Tests that composing rect updates are transformed from Flutter logical
146 // coordinates to device coordinates and passed to the text input manager
147 // when the DPI scale is 150% (144 DPI).
148 TEST(FlutterWindowTest, OnCursorRectUpdatedHighDPI) {
149  MockFlutterWindow win32window;
150  EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.5));
151 
152  Rect expected_cursor_rect(Point(15, 30), Size(45, 60));
153  EXPECT_CALL(win32window, UpdateCursorRect(expected_cursor_rect)).Times(1);
154 
155  Rect cursor_rect(Point(10, 20), Size(30, 40));
156  win32window.OnCursorRectUpdated(cursor_rect);
157 }
158 
159 TEST(FlutterWindowTest, OnPointerStarSendsDeviceType) {
160  FlutterWindow win32window(100, 100);
161  MockWindowBindingHandlerDelegate delegate;
162  EXPECT_CALL(delegate, OnWindowStateEvent).Times(AnyNumber());
163  win32window.SetView(&delegate);
164 
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  EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
264  win32window.SetView(&delegate);
265 
266  ON_CALL(win32window, GetScrollOffsetMultiplier())
267  .WillByDefault(Return(120.0f));
268  EXPECT_CALL(win32window, GetScrollOffsetMultiplier()).Times(1);
269 
270  EXPECT_CALL(delegate,
271  OnScroll(_, _, 0, 0, 120.0f, kFlutterPointerDeviceKindMouse,
272  kDefaultPointerDeviceId))
273  .Times(1);
274 
275  win32window.OnScroll(0.0f, 0.0f, kFlutterPointerDeviceKindMouse,
276  kDefaultPointerDeviceId);
277 }
278 
279 TEST(FlutterWindowTest, OnWindowRepaint) {
280  MockFlutterWindow win32window;
281  MockWindowBindingHandlerDelegate delegate;
282  EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
283  win32window.SetView(&delegate);
284 
285  EXPECT_CALL(delegate, OnWindowRepaint()).Times(1);
286 
287  win32window.InjectWindowMessage(WM_PAINT, 0, 0);
288 }
289 
290 TEST(FlutterWindowTest, OnThemeChange) {
291  MockFlutterWindow win32window;
292  MockWindowBindingHandlerDelegate delegate;
293  EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
294  win32window.SetView(&delegate);
295 
296  EXPECT_CALL(delegate, OnHighContrastChanged).Times(1);
297 
298  win32window.InjectWindowMessage(WM_THEMECHANGED, 0, 0);
299 }
300 
301 // The window should return no root accessibility node if
302 // it isn't attached to a view.
303 // Regression test for https://github.com/flutter/flutter/issues/129791
304 TEST(FlutterWindowTest, AccessibilityNodeWithoutView) {
305  MockFlutterWindow win32window;
306 
307  EXPECT_EQ(win32window.GetNativeViewAccessible(), nullptr);
308 }
309 
310 // Ensure that announcing the alert propagates the message to the alert node.
311 // Different screen readers use different properties for alerts.
312 TEST(FlutterWindowTest, AlertNode) {
313  std::unique_ptr<MockFlutterWindow> win32window =
314  std::make_unique<MockFlutterWindow>();
315  EXPECT_CALL(*win32window.get(), GetAxFragmentRootDelegate())
316  .WillRepeatedly(Return(nullptr));
317  EXPECT_CALL(*win32window.get(), OnWindowStateEvent).Times(AnyNumber());
318  MockFlutterWindowsView view(std::move(win32window));
319  std::wstring message = L"Test alert";
320  EXPECT_CALL(view, NotifyWinEventWrapper(_, ax::mojom::Event::kAlert))
321  .Times(1);
322  view.AnnounceAlert(message);
323 
324  IAccessible* alert = view.AlertNode();
325  VARIANT self{.vt = VT_I4, .lVal = CHILDID_SELF};
326  BSTR strptr;
327  alert->get_accName(self, &strptr);
328  EXPECT_EQ(message, strptr);
329 
330  alert->get_accDescription(self, &strptr);
331  EXPECT_EQ(message, strptr);
332 
333  alert->get_accValue(self, &strptr);
334  EXPECT_EQ(message, strptr);
335 
336  VARIANT role;
337  alert->get_accRole(self, &role);
338  EXPECT_EQ(role.vt, VT_I4);
339  EXPECT_EQ(role.lVal, ROLE_SYSTEM_ALERT);
340 }
341 
342 TEST(FlutterWindowTest, LifecycleFocusMessages) {
343  MockFlutterWindow win32window;
344  EXPECT_CALL(win32window, GetWindowHandle)
345  .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
346  MockWindowBindingHandlerDelegate delegate;
347 
348  WindowStateEvent last_event;
349  EXPECT_CALL(delegate, OnWindowStateEvent)
350  .WillRepeatedly([&last_event](HWND hwnd, WindowStateEvent event) {
351  last_event = event;
352  });
353  EXPECT_CALL(win32window, OnWindowStateEvent)
354  .WillRepeatedly([&](WindowStateEvent event) {
355  win32window.FlutterWindow::OnWindowStateEvent(event);
356  });
357  EXPECT_CALL(win32window, OnResize).Times(AnyNumber());
358 
359  win32window.SetView(&delegate);
360 
361  win32window.InjectWindowMessage(WM_SIZE, 0, 0);
362  EXPECT_EQ(last_event, WindowStateEvent::kHide);
363 
364  win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
365  EXPECT_EQ(last_event, WindowStateEvent::kShow);
366 
367  win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
368  EXPECT_EQ(last_event, WindowStateEvent::kFocus);
369 
370  win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0);
371  EXPECT_EQ(last_event, WindowStateEvent::kUnfocus);
372 }
373 
374 TEST(FlutterWindowTest, CachedLifecycleMessage) {
375  MockFlutterWindow win32window;
376  EXPECT_CALL(win32window, GetWindowHandle)
377  .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
378  EXPECT_CALL(win32window, OnWindowStateEvent)
379  .WillRepeatedly([&](WindowStateEvent event) {
380  win32window.FlutterWindow::OnWindowStateEvent(event);
381  });
382  EXPECT_CALL(win32window, OnResize).Times(1);
383 
384  // Restore
385  win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
386 
387  // Focus
388  win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
389 
390  MockWindowBindingHandlerDelegate delegate;
391  bool focused = false;
392  bool restored = false;
393  EXPECT_CALL(delegate, OnWindowStateEvent)
394  .WillRepeatedly([&](HWND hwnd, WindowStateEvent event) {
395  if (event == WindowStateEvent::kFocus) {
396  focused = true;
397  } else if (event == WindowStateEvent::kShow) {
398  restored = true;
399  }
400  });
401 
402  win32window.SetView(&delegate);
403  EXPECT_TRUE(focused);
404  EXPECT_TRUE(restored);
405 }
406 
407 TEST(FlutterWindowTest, PosthumousWindowMessage) {
408  MockWindowBindingHandlerDelegate delegate;
409  int msg_count = 0;
410  HWND hwnd;
411  EXPECT_CALL(delegate, OnWindowStateEvent)
412  .WillRepeatedly([&](HWND hwnd, WindowStateEvent event) { msg_count++; });
413 
414  {
415  MockFlutterWindow win32window(false);
416  EXPECT_CALL(win32window, GetWindowHandle).WillRepeatedly([&]() {
417  return win32window.FlutterWindow::GetWindowHandle();
418  });
419  EXPECT_CALL(win32window, OnWindowStateEvent)
420  .WillRepeatedly([&](WindowStateEvent event) {
421  win32window.FlutterWindow::OnWindowStateEvent(event);
422  });
423  EXPECT_CALL(win32window, OnResize).Times(AnyNumber());
424  win32window.SetView(&delegate);
425  win32window.InitializeChild("Title", 1, 1);
426  hwnd = win32window.GetWindowHandle();
427  SendMessage(hwnd, WM_SIZE, 0, MAKEWORD(1, 1));
428  SendMessage(hwnd, WM_SETFOCUS, 0, 0);
429 
430  // By setting this to zero before exiting the scope that contains
431  // win32window, and then checking its value afterwards, enforce that the
432  // window receive and process messages from its destructor without
433  // accessing out-of-bounds memory.
434  msg_count = 0;
435  }
436  EXPECT_GE(msg_count, 1);
437 }
438 
439 } // namespace testing
440 } // namespace flutter
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::FlutterWindow::GetDpiScale
virtual float GetDpiScale() override
Definition: flutter_window.cc:167
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(std::unique_ptr< WindowBindingHandler > wbh)
Definition: flutter_windows_engine_unittests.cc:621
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:311
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::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:235
message
Win32Message message
Definition: keyboard_unittests.cc:137
flutter::FlutterWindow
Definition: flutter_window.h:35
flutter::Size
Definition: geometry.h:33
flutter::FlutterWindowsView::FlutterWindowsView
FlutterWindowsView(std::unique_ptr< WindowBindingHandler > window_binding, std::shared_ptr< WindowsProcTable > windows_proc_table=nullptr)
Definition: flutter_windows_view.cc:72