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(void, OnSetCursor, (), (override));
70  MOCK_METHOD(float, GetScrollOffsetMultiplier, (), (override));
71  MOCK_METHOD(float, GetDpiScale, (), (override));
72  MOCK_METHOD(void, UpdateCursorRect, (const Rect&), (override));
73  MOCK_METHOD(void, OnResetImeComposing, (), (override));
74  MOCK_METHOD(UINT, Win32DispatchMessage, (UINT, WPARAM, LPARAM), (override));
75  MOCK_METHOD(BOOL, Win32PeekMessage, (LPMSG, UINT, UINT, UINT), (override));
76  MOCK_METHOD(uint32_t, Win32MapVkToChar, (uint32_t), (override));
77  MOCK_METHOD(HWND, GetWindowHandle, (), (override));
78  MOCK_METHOD(ui::AXFragmentRootDelegateWin*,
79  GetAxFragmentRootDelegate,
80  (),
81  (override));
82  MOCK_METHOD(void, OnWindowStateEvent, (WindowStateEvent), (override));
83 
84  protected:
85  // |KeyboardManager::WindowDelegate|
86  LRESULT Win32DefWindowProc(HWND hWnd,
87  UINT Msg,
88  WPARAM wParam,
89  LPARAM lParam) override {
90  return kWmResultDefault;
91  }
92 
93  private:
94  bool reset_view_on_exit_;
95  FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindow);
96 };
97 
98 class MockFlutterWindowsView : public FlutterWindowsView {
99  public:
100  MockFlutterWindowsView(FlutterWindowsEngine* engine,
101  std::unique_ptr<WindowBindingHandler> window_binding)
102  : FlutterWindowsView(kImplicitViewId, engine, std::move(window_binding)) {
103  }
105 
106  MOCK_METHOD(void,
107  NotifyWinEventWrapper,
108  (ui::AXPlatformNodeWin*, ax::mojom::Event),
109  (override));
110 
111  private:
112  FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView);
113 };
114 
115 class FlutterWindowTest : public WindowsTest {};
116 
117 } // namespace
118 
119 TEST_F(FlutterWindowTest, CreateDestroy) {
120  FlutterWindow window(800, 600);
121  ASSERT_TRUE(TRUE);
122 }
123 
124 TEST_F(FlutterWindowTest, OnBitmapSurfaceUpdated) {
125  FlutterWindow win32window(100, 100);
126  int old_handle_count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
127 
128  constexpr size_t row_bytes = 100 * 4;
129  constexpr size_t height = 100;
130  std::array<char, row_bytes * height> allocation;
131  win32window.OnBitmapSurfaceUpdated(allocation.data(), row_bytes, height);
132 
133  int new_handle_count = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
134  // Check GDI resources leak
135  EXPECT_EQ(old_handle_count, new_handle_count);
136 }
137 
138 // Tests that composing rect updates are transformed from Flutter logical
139 // coordinates to device coordinates and passed to the text input manager
140 // when the DPI scale is 100% (96 DPI).
141 TEST_F(FlutterWindowTest, OnCursorRectUpdatedRegularDPI) {
142  MockFlutterWindow win32window;
143  EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.0));
144 
145  Rect cursor_rect(Point(10, 20), Size(30, 40));
146  EXPECT_CALL(win32window, UpdateCursorRect(cursor_rect)).Times(1);
147 
148  win32window.OnCursorRectUpdated(cursor_rect);
149 }
150 
151 // Tests that composing rect updates are transformed from Flutter logical
152 // coordinates to device coordinates and passed to the text input manager
153 // when the DPI scale is 150% (144 DPI).
154 TEST_F(FlutterWindowTest, OnCursorRectUpdatedHighDPI) {
155  MockFlutterWindow win32window;
156  EXPECT_CALL(win32window, GetDpiScale()).WillOnce(Return(1.5));
157 
158  Rect expected_cursor_rect(Point(15, 30), Size(45, 60));
159  EXPECT_CALL(win32window, UpdateCursorRect(expected_cursor_rect)).Times(1);
160 
161  Rect cursor_rect(Point(10, 20), Size(30, 40));
162  win32window.OnCursorRectUpdated(cursor_rect);
163 }
164 
165 TEST_F(FlutterWindowTest, OnPointerStarSendsDeviceType) {
166  FlutterWindow win32window(100, 100);
167  MockWindowBindingHandlerDelegate delegate;
168  EXPECT_CALL(delegate, OnWindowStateEvent).Times(AnyNumber());
169  win32window.SetView(&delegate);
170 
171  // Move
172  EXPECT_CALL(delegate,
173  OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
174  kDefaultPointerDeviceId, 0))
175  .Times(1);
176  EXPECT_CALL(delegate,
177  OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
178  kDefaultPointerDeviceId, 0))
179  .Times(1);
180  EXPECT_CALL(delegate,
181  OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
182  kDefaultPointerDeviceId, 0))
183  .Times(1);
184 
185  // Down
186  EXPECT_CALL(
187  delegate,
188  OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
189  kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
190  .Times(1);
191  EXPECT_CALL(
192  delegate,
193  OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
194  kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
195  .Times(1);
196  EXPECT_CALL(
197  delegate,
198  OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
199  kDefaultPointerDeviceId, kFlutterPointerButtonMousePrimary))
200  .Times(1);
201 
202  // Up
203  EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
204  kDefaultPointerDeviceId,
205  kFlutterPointerButtonMousePrimary))
206  .Times(1);
207  EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
208  kDefaultPointerDeviceId,
209  kFlutterPointerButtonMousePrimary))
210  .Times(1);
211  EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
212  kDefaultPointerDeviceId,
213  kFlutterPointerButtonMousePrimary))
214  .Times(1);
215 
216  // Leave
217  EXPECT_CALL(delegate,
218  OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindMouse,
219  kDefaultPointerDeviceId))
220  .Times(1);
221  EXPECT_CALL(delegate,
222  OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindTouch,
223  kDefaultPointerDeviceId))
224  .Times(1);
225  EXPECT_CALL(delegate,
226  OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
227  kDefaultPointerDeviceId))
228  .Times(1);
229 
230  win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse,
231  kDefaultPointerDeviceId, 0);
232  win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse,
233  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
234  win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse,
235  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
236  win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindMouse,
237  kDefaultPointerDeviceId);
238 
239  // Touch
240  win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch,
241  kDefaultPointerDeviceId, 0);
242  win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch,
243  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
244  win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch,
245  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
246  win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindTouch,
247  kDefaultPointerDeviceId);
248 
249  // Pen
250  win32window.OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus,
251  kDefaultPointerDeviceId, 0);
252  win32window.OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus,
253  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
254  win32window.OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus,
255  kDefaultPointerDeviceId, WM_LBUTTONDOWN);
256  win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
257  kDefaultPointerDeviceId);
258 
259  // Destruction of win32window sends a HIDE update. In situ, the window is
260  // owned by the delegate, and so is destructed first. Not so here.
261  win32window.SetView(nullptr);
262 }
263 
264 // Tests that calls to OnScroll in turn calls GetScrollOffsetMultiplier
265 // for mapping scroll ticks to pixels.
266 TEST_F(FlutterWindowTest, OnScrollCallsGetScrollOffsetMultiplier) {
267  MockFlutterWindow win32window;
268  MockWindowBindingHandlerDelegate delegate;
269  EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
270  win32window.SetView(&delegate);
271 
272  EXPECT_CALL(win32window, GetWindowHandle).WillOnce([&win32window]() {
273  return win32window.FlutterWindow::GetWindowHandle();
274  });
275  EXPECT_CALL(win32window, GetScrollOffsetMultiplier).WillOnce(Return(120.0f));
276 
277  EXPECT_CALL(delegate,
278  OnScroll(_, _, 0, 0, 120.0f, kFlutterPointerDeviceKindMouse,
279  kDefaultPointerDeviceId))
280  .Times(1);
281 
282  win32window.OnScroll(0.0f, 0.0f, kFlutterPointerDeviceKindMouse,
283  kDefaultPointerDeviceId);
284 }
285 
286 TEST_F(FlutterWindowTest, OnWindowRepaint) {
287  MockFlutterWindow win32window;
288  MockWindowBindingHandlerDelegate delegate;
289  EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
290  win32window.SetView(&delegate);
291 
292  EXPECT_CALL(delegate, OnWindowRepaint()).Times(1);
293 
294  win32window.InjectWindowMessage(WM_PAINT, 0, 0);
295 }
296 
297 TEST_F(FlutterWindowTest, OnThemeChange) {
298  MockFlutterWindow win32window;
299  MockWindowBindingHandlerDelegate delegate;
300  EXPECT_CALL(win32window, OnWindowStateEvent).Times(AnyNumber());
301  win32window.SetView(&delegate);
302 
303  EXPECT_CALL(delegate, OnHighContrastChanged).Times(1);
304 
305  win32window.InjectWindowMessage(WM_THEMECHANGED, 0, 0);
306 }
307 
308 // The window should return no root accessibility node if
309 // it isn't attached to a view.
310 // Regression test for https://github.com/flutter/flutter/issues/129791
311 TEST_F(FlutterWindowTest, AccessibilityNodeWithoutView) {
312  MockFlutterWindow win32window;
313 
314  EXPECT_EQ(win32window.GetNativeViewAccessible(), nullptr);
315 }
316 
317 // Ensure that announcing the alert propagates the message to the alert node.
318 // Different screen readers use different properties for alerts.
319 TEST_F(FlutterWindowTest, AlertNode) {
320  std::unique_ptr<FlutterWindowsEngine> engine =
321  FlutterWindowsEngineBuilder{GetContext()}.Build();
322  auto win32window = std::make_unique<MockFlutterWindow>();
323  EXPECT_CALL(*win32window.get(), GetAxFragmentRootDelegate())
324  .WillRepeatedly(Return(nullptr));
325  EXPECT_CALL(*win32window.get(), OnWindowStateEvent).Times(AnyNumber());
326  EXPECT_CALL(*win32window.get(), GetWindowHandle).Times(AnyNumber());
327  MockFlutterWindowsView view{engine.get(), 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_F(FlutterWindowTest, LifecycleFocusMessages) {
352  MockFlutterWindow win32window;
353  EXPECT_CALL(win32window, GetWindowHandle)
354  .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
355  MockWindowBindingHandlerDelegate delegate;
356 
357  WindowStateEvent last_event;
358  EXPECT_CALL(delegate, OnWindowStateEvent)
359  .WillRepeatedly([&last_event](HWND hwnd, WindowStateEvent event) {
360  last_event = event;
361  });
362  EXPECT_CALL(win32window, OnWindowStateEvent)
363  .WillRepeatedly([&](WindowStateEvent event) {
364  win32window.FlutterWindow::OnWindowStateEvent(event);
365  });
366  EXPECT_CALL(win32window, OnResize).Times(AnyNumber());
367 
368  win32window.SetView(&delegate);
369 
370  win32window.InjectWindowMessage(WM_SIZE, 0, 0);
371  EXPECT_EQ(last_event, WindowStateEvent::kHide);
372 
373  win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
374  EXPECT_EQ(last_event, WindowStateEvent::kShow);
375 
376  EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kFocused),
377  Eq(FlutterViewFocusDirection::kUndefined)))
378  .Times(1);
379  win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
380  EXPECT_EQ(last_event, WindowStateEvent::kFocus);
381 
382  EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kUnfocused),
383  Eq(FlutterViewFocusDirection::kUndefined)))
384  .Times(1);
385  win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0);
386  EXPECT_EQ(last_event, WindowStateEvent::kUnfocus);
387 }
388 
389 TEST_F(FlutterWindowTest, CachedLifecycleMessage) {
390  MockFlutterWindow win32window;
391  EXPECT_CALL(win32window, GetWindowHandle)
392  .WillRepeatedly(Return(reinterpret_cast<HWND>(1)));
393  EXPECT_CALL(win32window, OnWindowStateEvent)
394  .WillRepeatedly([&](WindowStateEvent event) {
395  win32window.FlutterWindow::OnWindowStateEvent(event);
396  });
397  EXPECT_CALL(win32window, OnResize).Times(1);
398 
399  // Restore
400  win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
401 
402  // Focus
403  win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
404 
405  MockWindowBindingHandlerDelegate delegate;
406  bool focused = false;
407  bool restored = false;
408  EXPECT_CALL(delegate, OnWindowStateEvent)
409  .WillRepeatedly([&](HWND hwnd, WindowStateEvent event) {
410  if (event == WindowStateEvent::kFocus) {
411  focused = true;
412  } else if (event == WindowStateEvent::kShow) {
413  restored = true;
414  }
415  });
416 
417  EXPECT_CALL(delegate, OnFocus(Eq(FlutterViewFocusState::kFocused),
418  Eq(FlutterViewFocusDirection::kUndefined)))
419  .Times(1);
420  win32window.SetView(&delegate);
421  EXPECT_TRUE(focused);
422  EXPECT_TRUE(restored);
423 }
424 
425 TEST_F(FlutterWindowTest, UpdateCursor) {
426  FlutterWindow win32window(100, 100);
427  win32window.UpdateFlutterCursor("text");
428  HCURSOR cursor = ::GetCursor();
429  EXPECT_EQ(cursor, ::LoadCursor(nullptr, IDC_IBEAM));
430 }
431 
432 } // namespace testing
433 } // 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:227
flutter::testing::MockFlutterWindowsView::MockFlutterWindowsView
MockFlutterWindowsView(FlutterWindowsEngine *engine, std::unique_ptr< WindowBindingHandler > wbh)
Definition: flutter_windows_engine_unittests.cc:655
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:332
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::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