Flutter Windows Embedder
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 <array>
6 
7 #include "flutter/shell/platform/windows/testing/mock_direct_manipulation.h"
8 #include "flutter/shell/platform/windows/testing/mock_text_input_manager.h"
9 #include "flutter/shell/platform/windows/testing/mock_window.h"
10 #include "flutter/shell/platform/windows/testing/mock_windows_proc_table.h"
11 #include "gmock/gmock.h"
12 #include "gtest/gtest.h"
13 
14 using testing::_;
15 using testing::Eq;
16 using testing::InSequence;
17 using testing::Invoke;
18 using testing::Return;
19 
20 namespace flutter {
21 namespace testing {
22 
23 TEST(MockWindow, CreateDestroy) {
24  MockWindow window;
25  ASSERT_TRUE(TRUE);
26 }
27 
28 TEST(MockWindow, GetDpiAfterCreate) {
29  MockWindow window;
30  ASSERT_TRUE(window.GetDpi() > 0);
31 }
32 
33 TEST(MockWindow, VerticalScroll) {
34  MockWindow window;
35  const int scroll_amount = 10;
36  // Vertical scroll should be passed along, adjusted for scroll tick size
37  // and direction.
38  EXPECT_CALL(window, OnScroll(0, -scroll_amount / 120.0,
39  kFlutterPointerDeviceKindMouse, 0))
40  .Times(1);
41 
42  window.InjectWindowMessage(WM_MOUSEWHEEL, MAKEWPARAM(0, scroll_amount), 0);
43 }
44 
45 TEST(MockWindow, OnImeCompositionCompose) {
46  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
47  auto* text_input_manager = new MockTextInputManager();
48  std::unique_ptr<TextInputManager> text_input_manager_ptr(text_input_manager);
49  MockWindow window(std::move(windows_proc_table),
50  std::move(text_input_manager_ptr));
51  EXPECT_CALL(*text_input_manager, GetComposingString())
52  .WillRepeatedly(
53  Return(std::optional<std::u16string>(std::u16string(u"nihao"))));
54  EXPECT_CALL(*text_input_manager, GetResultString())
55  .WillRepeatedly(
56  Return(std::optional<std::u16string>(std::u16string(u"`}"))));
57  EXPECT_CALL(*text_input_manager, GetComposingCursorPosition()).Times(0);
58 
59  EXPECT_CALL(window, OnComposeChange(std::u16string(u"nihao"), 5)).Times(1);
60  EXPECT_CALL(window, OnComposeChange(std::u16string(u"`}"), 0)).Times(0);
61  EXPECT_CALL(window, OnComposeCommit()).Times(0);
62  ON_CALL(window, OnImeComposition)
63  .WillByDefault(Invoke(&window, &MockWindow::CallOnImeComposition));
64  EXPECT_CALL(window, OnImeComposition(_, _, _)).Times(1);
65 
66  // Send an IME_COMPOSITION event that contains just the composition string.
67  window.InjectWindowMessage(WM_IME_COMPOSITION, 0, GCS_COMPSTR);
68 }
69 
70 // Regression test for https://github.com/flutter/flutter/issues/140739.
71 TEST(MockWindow, OnImeCompositionDefaultsKoreanCursorToCompositionEnd) {
72  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
73  auto* text_input_manager = new MockTextInputManager();
74  std::unique_ptr<TextInputManager> text_input_manager_ptr(text_input_manager);
75  MockWindow window(std::move(windows_proc_table),
76  std::move(text_input_manager_ptr));
77  EXPECT_CALL(*text_input_manager, GetComposingString())
78  .WillRepeatedly(
79  Return(std::optional<std::u16string>(std::u16string(u"다"))));
80  EXPECT_CALL(*text_input_manager, GetComposingCursorPosition()).Times(0);
81 
82  EXPECT_CALL(window, OnComposeChange(std::u16string(u"다"), 1)).Times(1);
83  EXPECT_CALL(window, OnComposeCommit()).Times(0);
84  ON_CALL(window, OnImeComposition)
85  .WillByDefault(Invoke(&window, &MockWindow::CallOnImeComposition));
86  EXPECT_CALL(window, OnImeComposition(_, _, _)).Times(1);
87 
88  window.InjectWindowMessage(WM_IME_COMPOSITION, 0, GCS_COMPSTR);
89 }
90 
91 TEST(MockWindow, OnImeCompositionResult) {
92  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
93  auto* text_input_manager = new MockTextInputManager();
94  std::unique_ptr<TextInputManager> text_input_manager_ptr(text_input_manager);
95  MockWindow window(std::move(windows_proc_table),
96  std::move(text_input_manager_ptr));
97  EXPECT_CALL(*text_input_manager, GetComposingString())
98  .WillRepeatedly(
99  Return(std::optional<std::u16string>(std::u16string(u"nihao"))));
100  EXPECT_CALL(*text_input_manager, GetResultString())
101  .WillRepeatedly(
102  Return(std::optional<std::u16string>(std::u16string(u"`}"))));
103  EXPECT_CALL(*text_input_manager, GetComposingCursorPosition()).Times(0);
104 
105  EXPECT_CALL(window, OnComposeChange(std::u16string(u"nihao"), 0)).Times(0);
106  EXPECT_CALL(window, OnComposeChange(std::u16string(u"`}"), 2)).Times(1);
107  EXPECT_CALL(window, OnComposeCommit()).Times(1);
108  ON_CALL(window, OnImeComposition)
109  .WillByDefault(Invoke(&window, &MockWindow::CallOnImeComposition));
110  EXPECT_CALL(window, OnImeComposition(_, _, _)).Times(1);
111 
112  // Send an IME_COMPOSITION event that contains just the result string.
113  window.InjectWindowMessage(WM_IME_COMPOSITION, 0, GCS_RESULTSTR);
114 }
115 
116 TEST(MockWindow, OnImeCompositionResultAndCompose) {
117  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
118  auto* text_input_manager = new MockTextInputManager();
119  std::unique_ptr<TextInputManager> text_input_manager_ptr(text_input_manager);
120  MockWindow window(std::move(windows_proc_table),
121  std::move(text_input_manager_ptr));
122 
123  // This situation is that Google Japanese Input finished composing "今日" in
124  // "今日は" but is still composing "は".
125  {
126  InSequence dummy;
127  EXPECT_CALL(*text_input_manager, GetResultString())
128  .WillRepeatedly(
129  Return(std::optional<std::u16string>(std::u16string(u"今日"))));
130  EXPECT_CALL(*text_input_manager, GetComposingString())
131  .WillRepeatedly(
132  Return(std::optional<std::u16string>(std::u16string(u"は"))));
133  }
134  {
135  InSequence dummy;
136  EXPECT_CALL(window, OnComposeChange(std::u16string(u"今日"), 2)).Times(1);
137  EXPECT_CALL(window, OnComposeCommit()).Times(1);
138  EXPECT_CALL(window, OnComposeChange(std::u16string(u"は"), 1)).Times(1);
139  }
140 
141  EXPECT_CALL(*text_input_manager, GetComposingCursorPosition()).Times(0);
142 
143  ON_CALL(window, OnImeComposition)
144  .WillByDefault(Invoke(&window, &MockWindow::CallOnImeComposition));
145  EXPECT_CALL(window, OnImeComposition(_, _, _)).Times(1);
146 
147  // send an IME_COMPOSITION event that contains both the result string and the
148  // composition string.
149  window.InjectWindowMessage(WM_IME_COMPOSITION, 0,
150  GCS_COMPSTR | GCS_RESULTSTR);
151 }
152 
153 TEST(MockWindow, OnImeCompositionUsesCursorPositionWhenProvided) {
154  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
155  auto* text_input_manager = new MockTextInputManager();
156  std::unique_ptr<TextInputManager> text_input_manager_ptr(text_input_manager);
157  MockWindow window(std::move(windows_proc_table),
158  std::move(text_input_manager_ptr));
159  EXPECT_CALL(*text_input_manager, GetComposingString())
160  .WillRepeatedly(
161  Return(std::optional<std::u16string>(std::u16string(u"nihao"))));
162  EXPECT_CALL(*text_input_manager, GetComposingCursorPosition())
163  .WillOnce(Return(2));
164 
165  EXPECT_CALL(window, OnComposeChange(std::u16string(u"nihao"), 2)).Times(1);
166  EXPECT_CALL(window, OnComposeCommit()).Times(0);
167  ON_CALL(window, OnImeComposition)
168  .WillByDefault(Invoke(&window, &MockWindow::CallOnImeComposition));
169  EXPECT_CALL(window, OnImeComposition(_, _, _)).Times(1);
170 
171  window.InjectWindowMessage(WM_IME_COMPOSITION, 0,
172  GCS_COMPSTR | GCS_CURSORPOS);
173 }
174 
175 TEST(MockWindow, OnImeCompositionFallsBackToTextEndForInvalidCursorPosition) {
176  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
177  auto* text_input_manager = new MockTextInputManager();
178  std::unique_ptr<TextInputManager> text_input_manager_ptr(text_input_manager);
179  MockWindow window(std::move(windows_proc_table),
180  std::move(text_input_manager_ptr));
181  EXPECT_CALL(*text_input_manager, GetComposingString())
182  .WillRepeatedly(
183  Return(std::optional<std::u16string>(std::u16string(u"nihao"))));
184  EXPECT_CALL(*text_input_manager, GetComposingCursorPosition())
185  .WillOnce(Return(-1));
186 
187  EXPECT_CALL(window, OnComposeChange(std::u16string(u"nihao"), 5)).Times(1);
188  EXPECT_CALL(window, OnComposeCommit()).Times(0);
189  ON_CALL(window, OnImeComposition)
190  .WillByDefault(Invoke(&window, &MockWindow::CallOnImeComposition));
191  EXPECT_CALL(window, OnImeComposition(_, _, _)).Times(1);
192 
193  window.InjectWindowMessage(WM_IME_COMPOSITION, 0,
194  GCS_COMPSTR | GCS_CURSORPOS);
195 }
196 
197 TEST(MockWindow, OnImeCompositionClearChange) {
198  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
199  auto* text_input_manager = new MockTextInputManager();
200  std::unique_ptr<TextInputManager> text_input_manager_ptr(text_input_manager);
201  MockWindow window(std::move(windows_proc_table),
202  std::move(text_input_manager_ptr));
203  EXPECT_CALL(window, OnComposeChange(std::u16string(u""), 0)).Times(1);
204  EXPECT_CALL(window, OnComposeCommit()).Times(1);
205  ON_CALL(window, OnImeComposition)
206  .WillByDefault(Invoke(&window, &MockWindow::CallOnImeComposition));
207  EXPECT_CALL(window, OnImeComposition(_, _, _)).Times(1);
208 
209  // send an IME_COMPOSITION event that contains both the result string and the
210  // composition string.
211  window.InjectWindowMessage(WM_IME_COMPOSITION, 0, 0);
212 }
213 
214 TEST(MockWindow, HorizontalScroll) {
215  MockWindow window;
216  const int scroll_amount = 10;
217  // Vertical scroll should be passed along, adjusted for scroll tick size.
218  EXPECT_CALL(window, OnScroll(scroll_amount / 120.0, 0,
219  kFlutterPointerDeviceKindMouse, 0))
220  .Times(1);
221 
222  window.InjectWindowMessage(WM_MOUSEHWHEEL, MAKEWPARAM(0, scroll_amount), 0);
223 }
224 
225 TEST(MockWindow, MouseLeave) {
226  MockWindow window;
227  const double mouse_x = 10.0;
228  const double mouse_y = 20.0;
229 
230  EXPECT_CALL(window,
231  OnPointerMove(mouse_x, mouse_y, kFlutterPointerDeviceKindMouse, 0,
232  0, 0, 0, 0))
233  .Times(1);
234  EXPECT_CALL(window, OnPointerLeave(mouse_x, mouse_y,
235  kFlutterPointerDeviceKindMouse, 0))
236  .Times(1);
237 
238  window.InjectWindowMessage(WM_MOUSEMOVE, 0, MAKELPARAM(mouse_x, mouse_y));
239  window.InjectWindowMessage(WM_MOUSELEAVE, 0, 0);
240 }
241 
242 TEST(MockWindow, KeyDown) {
243  MockWindow window;
244  EXPECT_CALL(window, OnKey(_, _, _, _, _, _, _)).Times(1);
245  LPARAM lparam = CreateKeyEventLparam(42, false, false);
246  // send a "Shift" key down event.
247  window.InjectWindowMessage(WM_KEYDOWN, 16, lparam);
248 }
249 
250 TEST(MockWindow, KeyUp) {
251  MockWindow window;
252  EXPECT_CALL(window, OnKey(_, _, _, _, _, _, _)).Times(1);
253  LPARAM lparam = CreateKeyEventLparam(42, false, true);
254  // send a "Shift" key up event.
255  window.InjectWindowMessage(WM_KEYUP, 16, lparam);
256 }
257 
258 TEST(MockWindow, SysKeyDown) {
259  MockWindow window;
260  EXPECT_CALL(window, OnKey(_, _, _, _, _, _, _)).Times(1);
261  LPARAM lparam = CreateKeyEventLparam(42, false, false);
262  // send a "Shift" key down event.
263  window.InjectWindowMessage(WM_SYSKEYDOWN, 16, lparam);
264 }
265 
266 TEST(MockWindow, SysKeyUp) {
267  MockWindow window;
268  EXPECT_CALL(window, OnKey(_, _, _, _, _, _, _)).Times(1);
269  // Prevent default proc for WM_SYSKEYUP which unfocuses the window and sends
270  // WM_MOUSELEAVE.
271  EXPECT_CALL(window, Win32DefWindowProc(_, _, _, _)).Times(0);
272  LPARAM lparam = CreateKeyEventLparam(42, false, true);
273  // send a "Shift" key up event.
274  window.InjectWindowMessage(WM_SYSKEYUP, 16, lparam);
275 }
276 
277 TEST(MockWindow, KeyDownPrintable) {
278  MockWindow window;
279  LPARAM lparam = CreateKeyEventLparam(30, false, false);
280 
281  auto respond_false = [](int key, int scancode, int action, char32_t character,
282  bool extended, bool was_down,
283  std::function<void(bool)> callback) {
284  callback(false);
285  };
286  EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 0, false, false, _))
287  .Times(1)
288  .WillOnce(respond_false);
289  EXPECT_CALL(window, OnText(_)).Times(1);
290  std::array<Win32Message, 2> messages = {
291  Win32Message{WM_KEYDOWN, 65, lparam, kWmResultDontCheck},
292  Win32Message{WM_CHAR, 65, lparam, kWmResultDontCheck}};
293  window.InjectMessageList(2, messages.data());
294 }
295 
296 TEST(MockWindow, KeyDownWithCtrl) {
297  MockWindow window;
298 
299  // Simulate CONTROL pressed
300  std::array<BYTE, 256> keyboard_state;
301  keyboard_state[VK_CONTROL] = -1;
302  SetKeyboardState(keyboard_state.data());
303 
304  LPARAM lparam = CreateKeyEventLparam(30, false, false);
305 
306  // Expect OnKey, but not OnText, because Control + Key is not followed by
307  // WM_CHAR
308  EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 0, false, false, _)).Times(1);
309  EXPECT_CALL(window, OnText(_)).Times(0);
310 
311  window.InjectWindowMessage(WM_KEYDOWN, 65, lparam);
312 
313  keyboard_state.fill(0);
314  SetKeyboardState(keyboard_state.data());
315 }
316 
317 TEST(MockWindow, KeyDownWithCtrlToggled) {
318  MockWindow window;
319 
320  auto respond_false = [](int key, int scancode, int action, char32_t character,
321  bool extended, bool was_down,
322  std::function<void(bool)> callback) {
323  callback(false);
324  };
325 
326  // Simulate CONTROL toggled
327  std::array<BYTE, 256> keyboard_state;
328  keyboard_state[VK_CONTROL] = 1;
329  SetKeyboardState(keyboard_state.data());
330 
331  LPARAM lparam = CreateKeyEventLparam(30, false, false);
332 
333  EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 0, false, false, _))
334  .Times(1)
335  .WillOnce(respond_false);
336  EXPECT_CALL(window, OnText(_)).Times(1);
337 
338  // send a "A" key down event.
339  Win32Message messages[] = {{WM_KEYDOWN, 65, lparam, kWmResultDontCheck},
340  {WM_CHAR, 65, lparam, kWmResultDontCheck}};
341  window.InjectMessageList(2, messages);
342 
343  keyboard_state.fill(0);
344  SetKeyboardState(keyboard_state.data());
345 }
346 
347 TEST(MockWindow, Paint) {
348  MockWindow window;
349  EXPECT_CALL(window, OnPaint()).Times(1);
350  window.InjectWindowMessage(WM_PAINT, 0, 0);
351 }
352 
353 // Verify direct manipulation isn't notified of pointer hit tests.
354 TEST(MockWindow, PointerHitTest) {
355  UINT32 pointer_id = 123;
356  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
357  auto text_input_manager = std::make_unique<MockTextInputManager>();
358 
359  EXPECT_CALL(*windows_proc_table, GetPointerType(Eq(pointer_id), _))
360  .Times(1)
361  .WillOnce([](UINT32 pointer_id, POINTER_INPUT_TYPE* type) {
362  *type = PT_POINTER;
363  return TRUE;
364  });
365 
366  MockWindow window(std::move(windows_proc_table),
367  std::move(text_input_manager));
368 
369  auto direct_manipulation =
370  std::make_unique<MockDirectManipulationOwner>(&window);
371 
372  EXPECT_CALL(*direct_manipulation, SetContact).Times(0);
373 
374  window.SetDirectManipulationOwner(std::move(direct_manipulation));
375  window.InjectWindowMessage(DM_POINTERHITTEST, MAKEWPARAM(pointer_id, 0), 0);
376 }
377 
378 // Verify direct manipulation is notified of touchpad hit tests.
379 TEST(MockWindow, TouchPadHitTest) {
380  UINT32 pointer_id = 123;
381  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
382  auto text_input_manager = std::make_unique<MockTextInputManager>();
383 
384  EXPECT_CALL(*windows_proc_table, GetPointerType(Eq(pointer_id), _))
385  .Times(1)
386  .WillOnce([](UINT32 pointer_id, POINTER_INPUT_TYPE* type) {
387  *type = PT_TOUCHPAD;
388  return TRUE;
389  });
390 
391  MockWindow window(std::move(windows_proc_table),
392  std::move(text_input_manager));
393 
394  auto direct_manipulation =
395  std::make_unique<MockDirectManipulationOwner>(&window);
396 
397  EXPECT_CALL(*direct_manipulation, SetContact(Eq(pointer_id))).Times(1);
398 
399  window.SetDirectManipulationOwner(std::move(direct_manipulation));
400  window.InjectWindowMessage(DM_POINTERHITTEST, MAKEWPARAM(pointer_id, 0), 0);
401 }
402 
403 // Verify direct manipulation isn't notified of unknown hit tests.
404 // This can happen if determining the pointer type fails, for example,
405 // if GetPointerType is unsupported by the current Windows version.
406 // See: https://github.com/flutter/flutter/issues/109412
407 TEST(MockWindow, UnknownPointerTypeSkipsDirectManipulation) {
408  UINT32 pointer_id = 123;
409  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
410  auto text_input_manager = std::make_unique<MockTextInputManager>();
411 
412  EXPECT_CALL(*windows_proc_table, GetPointerType(Eq(pointer_id), _))
413  .Times(1)
414  .WillOnce(
415  [](UINT32 pointer_id, POINTER_INPUT_TYPE* type) { return FALSE; });
416 
417  MockWindow window(std::move(windows_proc_table),
418  std::move(text_input_manager));
419 
420  auto direct_manipulation =
421  std::make_unique<MockDirectManipulationOwner>(&window);
422 
423  EXPECT_CALL(*direct_manipulation, SetContact).Times(0);
424 
425  window.SetDirectManipulationOwner(std::move(direct_manipulation));
426  window.InjectWindowMessage(DM_POINTERHITTEST, MAKEWPARAM(pointer_id, 0), 0);
427 }
428 
429 // Test that the root UIA object is queried by WM_GETOBJECT.
430 TEST(MockWindow, DISABLED_GetObjectUia) {
431  MockWindow window;
432  bool uia_called = false;
433  ON_CALL(window, OnGetObject)
434  .WillByDefault([&uia_called](UINT msg, WPARAM wpar, LPARAM lpar) {
435 #ifdef FLUTTER_ENGINE_USE_UIA
436  uia_called = true;
437 #endif // FLUTTER_ENGINE_USE_UIA
438  return static_cast<LRESULT>(0);
439  });
440  EXPECT_CALL(window, OnGetObject).Times(1);
441 
442  window.InjectWindowMessage(WM_GETOBJECT, 0, UiaRootObjectId);
443 
444  EXPECT_TRUE(uia_called);
445 }
446 
447 } // namespace testing
448 } // namespace flutter
FlutterDesktopBinaryReply callback
enum flutter::testing::@104::KeyboardChange::Type type
TEST(AccessibilityBridgeWindows, GetParent)