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"
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, engine, std::move(window_binding)) {
102  }
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  EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kFocused),
376  Eq(FlutterViewFocusDirection::kUndefined)))
377  .Times(1);
378  win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
379  EXPECT_EQ(last_event, WindowStateEvent::kFocus);
380 
381  EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kUnfocused),
382  Eq(FlutterViewFocusDirection::kUndefined)))
383  .Times(1);
384  win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0);
385  EXPECT_EQ(last_event, WindowStateEvent::kUnfocus);
386 }
387 
388 TEST_F(FlutterWindowTest, CachedLifecycleMessage) {
389  MockFlutterWindow win32window;
390  EXPECT_CALL(win32window, GetWindowHandle)
391  .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
392  EXPECT_CALL(win32window, OnWindowStateEvent)
393  .WillRepeatedly([&](WindowStateEvent event) {
394  win32window.FlutterWindow::OnWindowStateEvent(event);
395  });
396  EXPECT_CALL(win32window, OnResize).Times(1);
397 
398  // Restore
399  win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
400 
401  // Focus
402  win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
403 
404  MockWindowBindingHandlerDelegate delegate;
405  bool focused = false;
406  bool restored = false;
407  EXPECT_CALL(delegate, OnWindowStateEvent)
408  .WillRepeatedly([&](HWND hwnd, WindowStateEvent event) {
409  if (event == WindowStateEvent::kFocus) {
410  focused = true;
411  } else if (event == WindowStateEvent::kShow) {
412  restored = true;
413  }
414  });
415 
416  EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kFocused),
417  Eq(FlutterViewFocusDirection::kUndefined)))
418  .Times(1);
419  win32window.SetView(&delegate);
420  EXPECT_TRUE(focused);
421  EXPECT_TRUE(restored);
422 }
423 
424 } // namespace testing
425 } // namespace flutter
virtual void OnPointerLeave(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id)
virtual bool OnBitmapSurfaceUpdated(const void *allocation, size_t row_bytes, size_t height) override
virtual void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button)
virtual void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, UINT button)
virtual void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id, int modifiers_state)
virtual void SetView(WindowBindingHandlerDelegate *view) override
virtual float GetDpiScale() override
FlutterWindowsView(FlutterViewId view_id, FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > window_binding, std::shared_ptr< WindowsProcTable > windows_proc_table=nullptr)
MOCK_METHOD(void, NotifyWinEventWrapper,(ui::AXPlatformNodeWin *, ax::mojom::Event),(override))
MockFlutterWindowsView(FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > wbh)
Win32Message message
TEST_F(CompositorOpenGLTest, CreateBackingStore)
WindowStateEvent
An event representing a change in window state that may update the.
constexpr FlutterViewId kImplicitViewId