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  std::unique_ptr<FlutterWindowsEngine> engine =
120  FlutterWindowsEngineBuilder{GetContext()}.Build();
121  FlutterWindow window(800, 600, engine->display_manager());
122  ASSERT_TRUE(TRUE);
123 }
124 
125 TEST_F(FlutterWindowTest, OnBitmapSurfaceUpdated) {
126  std::unique_ptr<FlutterWindowsEngine> engine =
127  FlutterWindowsEngineBuilder{GetContext()}.Build();
128  FlutterWindow win32window(100, 100, engine->display_manager());
129  int old_handle_count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
130 
131  constexpr size_t row_bytes = 100 * 4;
132  constexpr size_t height = 100;
133  std::array<char, row_bytes * height> allocation;
134  win32window.OnBitmapSurfaceUpdated(allocation.data(), row_bytes, height);
135 
136  int new_handle_count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
137  // Check GDI resources leak
138  EXPECT_EQ(old_handle_count, new_handle_count);
139 }
140 
141 // Tests that composing rect updates are transformed from Flutter logical
142 // coordinates to device coordinates and passed to the text input manager
143 // when the DPI scale is 100% (96 DPI).
144 TEST_F(FlutterWindowTest, OnCursorRectUpdatedRegularDPI) {
145  MockFlutterWindow win32window;
146  EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.0));
147 
148  Rect cursor_rect(Point(10, 20), Size(30, 40));
149  EXPECT_CALL(win32window, UpdateCursorRect(cursor_rect)).Times(1);
150 
151  win32window.OnCursorRectUpdated(cursor_rect);
152 }
153 
154 // Tests that composing rect updates are transformed from Flutter logical
155 // coordinates to device coordinates and passed to the text input manager
156 // when the DPI scale is 150% (144 DPI).
157 TEST_F(FlutterWindowTest, OnCursorRectUpdatedHighDPI) {
158  MockFlutterWindow win32window;
159  EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.5));
160 
161  Rect expected_cursor_rect(Point(15, 30), Size(45, 60));
162  EXPECT_CALL(win32window, UpdateCursorRect(expected_cursor_rect)).Times(1);
163 
164  Rect cursor_rect(Point(10, 20), Size(30, 40));
165  win32window.OnCursorRectUpdated(cursor_rect);
166 }
167 
168 TEST_F(FlutterWindowTest, OnPointerStarSendsDeviceType) {
169  std::unique_ptr<FlutterWindowsEngine> engine =
170  FlutterWindowsEngineBuilder{GetContext()}.Build();
171  FlutterWindow win32window(100, 100, engine->display_manager());
172  MockWindowBindingHandlerDelegate delegate;
173  EXPECT_CALL(delegate, OnWindowStateEvent).Times(AnyNumber());
174  win32window.SetView(&delegate);
175 
176  // Move
177  EXPECT_CALL(delegate,
178  OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
179  kDefaultPointerDeviceId, 0))
180  .Times(1);
181  EXPECT_CALL(delegate,
182  OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
183  kDefaultPointerDeviceId, 0))
184  .Times(1);
185  EXPECT_CALL(delegate,
186  OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
187  kDefaultPointerDeviceId, 0))
188  .Times(1);
189 
190  // Down
191  EXPECT_CALL(
192  delegate,
193  OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
194  kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
195  .Times(1);
196  EXPECT_CALL(
197  delegate,
198  OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
199  kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
200  .Times(1);
201  EXPECT_CALL(
202  delegate,
203  OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
204  kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
205  .Times(1);
206 
207  // Up
208  EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
209  kDefaultPointerDeviceId,
210  kFlutterPointerButtonMousePrimary))
211  .Times(1);
212  EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
213  kDefaultPointerDeviceId,
214  kFlutterPointerButtonMousePrimary))
215  .Times(1);
216  EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
217  kDefaultPointerDeviceId,
218  kFlutterPointerButtonMousePrimary))
219  .Times(1);
220 
221  // Leave
222  EXPECT_CALL(delegate,
223  OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindMouse,
224  kDefaultPointerDeviceId))
225  .Times(1);
226  EXPECT_CALL(delegate,
227  OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindTouch,
228  kDefaultPointerDeviceId))
229  .Times(1);
230  EXPECT_CALL(delegate,
231  OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
232  kDefaultPointerDeviceId))
233  .Times(1);
234 
235  win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
236  kDefaultPointerDeviceId, 0);
237  win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
238  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
239  win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
240  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
241  win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindMouse,
242  kDefaultPointerDeviceId);
243 
244  // Touch
245  win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
246  kDefaultPointerDeviceId, 0);
247  win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
248  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
249  win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
250  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
251  win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindTouch,
252  kDefaultPointerDeviceId);
253 
254  // Pen
255  win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
256  kDefaultPointerDeviceId, 0);
257  win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
258  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
259  win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
260  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
261  win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
262  kDefaultPointerDeviceId);
263 
264  // Destruction of win32window sends a HIDE update. In situ, the window is
265  // owned by the delegate, and so is destructed first. Not so here.
266  win32window.SetView(nullptr);
267 }
268 
269 // Tests that calls to OnScroll in turn calls GetScrollOffsetMultiplier
270 // for mapping scroll ticks to pixels.
271 TEST_F(FlutterWindowTest, OnScrollCallsGetScrollOffsetMultiplier) {
272  MockFlutterWindow win32window;
273  MockWindowBindingHandlerDelegate delegate;
274  EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
275  win32window.SetView(&delegate);
276 
277  EXPECT_CALL(win32window, GetWindowHandle).WillOnce([&win32window]() {
278  return win32window.FlutterWindow::GetWindowHandle();
279  });
280  EXPECT_CALL(win32window, GetScrollOffsetMultiplier).WillOnce(Return(120.0f));
281 
282  EXPECT_CALL(delegate,
283  OnScroll(_, _, 0, 0, 120.0f, kFlutterPointerDeviceKindMouse,
284  kDefaultPointerDeviceId))
285  .Times(1);
286 
287  win32window.OnScroll(0.0f, 0.0f, kFlutterPointerDeviceKindMouse,
288  kDefaultPointerDeviceId);
289 }
290 
291 TEST_F(FlutterWindowTest, OnWindowRepaint) {
292  MockFlutterWindow win32window;
293  MockWindowBindingHandlerDelegate delegate;
294  EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
295  win32window.SetView(&delegate);
296 
297  EXPECT_CALL(delegate, OnWindowRepaint()).Times(1);
298 
299  win32window.InjectWindowMessage(WM_PAINT, 0, 0);
300 }
301 
302 TEST_F(FlutterWindowTest, OnThemeChange) {
303  MockFlutterWindow win32window;
304  MockWindowBindingHandlerDelegate delegate;
305  EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
306  win32window.SetView(&delegate);
307 
308  EXPECT_CALL(delegate, OnHighContrastChanged).Times(1);
309 
310  win32window.InjectWindowMessage(WM_THEMECHANGED, 0, 0);
311 }
312 
313 // The window should return no root accessibility node if
314 // it isn't attached to a view.
315 // Regression test for https://github.com/flutter/flutter/issues/129791
316 TEST_F(FlutterWindowTest, AccessibilityNodeWithoutView) {
317  MockFlutterWindow win32window;
318 
319  EXPECT_EQ(win32window.GetNativeViewAccessible(), nullptr);
320 }
321 
322 // Ensure that announcing the alert propagates the message to the alert node.
323 // Different screen readers use different properties for alerts.
324 TEST_F(FlutterWindowTest, AlertNode) {
325  std::unique_ptr<FlutterWindowsEngine> engine =
326  FlutterWindowsEngineBuilder{GetContext()}.Build();
327  auto win32window = std::make_unique<MockFlutterWindow>();
328  EXPECT_CALL(*win32window.get(), GetAxFragmentRootDelegate())
329  .WillRepeatedly(Return(nullptr));
330  EXPECT_CALL(*win32window.get(), OnWindowStateEvent).Times(AnyNumber());
331  EXPECT_CALL(*win32window.get(), GetWindowHandle).Times(AnyNumber());
332  MockFlutterWindowsView view{engine.get(), std::move(win32window)};
333  std::wstring message = L"Test alert";
334  EXPECT_CALL(view, NotifyWinEventWrapper(_, ax::mojom::Event::kAlert))
335  .Times(1);
336  view.AnnounceAlert(message);
337 
338  IAccessible* alert = view.AlertNode();
339  VARIANT self{.vt = VT_I4, .lVal = CHILDID_SELF};
340  BSTR strptr;
341  alert->get_accName(self, &strptr);
342  EXPECT_EQ(message, strptr);
343 
344  alert->get_accDescription(self, &strptr);
345  EXPECT_EQ(message, strptr);
346 
347  alert->get_accValue(self, &strptr);
348  EXPECT_EQ(message, strptr);
349 
350  VARIANT role;
351  alert->get_accRole(self, &role);
352  EXPECT_EQ(role.vt, VT_I4);
353  EXPECT_EQ(role.lVal, ROLE_SYSTEM_ALERT);
354 }
355 
356 TEST_F(FlutterWindowTest, LifecycleFocusMessages) {
357  MockFlutterWindow win32window;
358  EXPECT_CALL(win32window, GetWindowHandle)
359  .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
360  MockWindowBindingHandlerDelegate delegate;
361 
362  WindowStateEvent last_event;
363  EXPECT_CALL(delegate, OnWindowStateEvent)
364  .WillRepeatedly([&last_event](HWND hwnd, WindowStateEvent event) {
365  last_event = event;
366  });
367  EXPECT_CALL(win32window, OnWindowStateEvent)
368  .WillRepeatedly([&](WindowStateEvent event) {
369  win32window.FlutterWindow::OnWindowStateEvent(event);
370  });
371  EXPECT_CALL(win32window, OnResize).Times(AnyNumber());
372 
373  win32window.SetView(&delegate);
374 
375  win32window.InjectWindowMessage(WM_SIZE, 0, 0);
376  EXPECT_EQ(last_event, WindowStateEvent::kHide);
377 
378  win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
379  EXPECT_EQ(last_event, WindowStateEvent::kShow);
380 
381  EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kFocused),
382  Eq(FlutterViewFocusDirection::kUndefined)))
383  .Times(1);
384  win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
385  EXPECT_EQ(last_event, WindowStateEvent::kFocus);
386 
387  EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kUnfocused),
388  Eq(FlutterViewFocusDirection::kUndefined)))
389  .Times(1);
390  win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0);
391  EXPECT_EQ(last_event, WindowStateEvent::kUnfocus);
392 }
393 
394 TEST_F(FlutterWindowTest, CachedLifecycleMessage) {
395  MockFlutterWindow win32window;
396  EXPECT_CALL(win32window, GetWindowHandle)
397  .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
398  EXPECT_CALL(win32window, OnWindowStateEvent)
399  .WillRepeatedly([&](WindowStateEvent event) {
400  win32window.FlutterWindow::OnWindowStateEvent(event);
401  });
402  EXPECT_CALL(win32window, OnResize).Times(1);
403 
404  // Restore
405  win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
406 
407  // Focus
408  win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
409 
410  MockWindowBindingHandlerDelegate delegate;
411  bool focused = false;
412  bool restored = false;
413  EXPECT_CALL(delegate, OnWindowStateEvent)
414  .WillRepeatedly([&](HWND hwnd, WindowStateEvent event) {
415  if (event == WindowStateEvent::kFocus) {
416  focused = true;
417  } else if (event == WindowStateEvent::kShow) {
418  restored = true;
419  }
420  });
421 
422  EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kFocused),
423  Eq(FlutterViewFocusDirection::kUndefined)))
424  .Times(1);
425  win32window.SetView(&delegate);
426  EXPECT_TRUE(focused);
427  EXPECT_TRUE(restored);
428 }
429 
430 } // namespace testing
431 } // 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