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::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  }
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