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())
58  .WillRepeatedly(Return((int)0));
59 
60  EXPECT_CALL(window, OnComposeChange(std::u16string(u"nihao"), 0)).Times(1);
61  EXPECT_CALL(window, OnComposeChange(std::u16string(u"`}"), 0)).Times(0);
62  EXPECT_CALL(window, OnComposeCommit()).Times(0);
63  ON_CALL(window, OnImeComposition)
64  .WillByDefault(Invoke(&window, &MockWindow::CallOnImeComposition));
65  EXPECT_CALL(window, OnImeComposition(_, _, _)).Times(1);
66 
67  // Send an IME_COMPOSITION event that contains just the composition string.
68  window.InjectWindowMessage(WM_IME_COMPOSITION, 0, GCS_COMPSTR);
69 }
70 
71 TEST(MockWindow, OnImeCompositionResult) {
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"nihao"))));
80  EXPECT_CALL(*text_input_manager, GetResultString())
81  .WillRepeatedly(
82  Return(std::optional<std::u16string>(std::u16string(u"`}"))));
83  EXPECT_CALL(*text_input_manager, GetComposingCursorPosition())
84  .WillRepeatedly(Return((int)0));
85 
86  EXPECT_CALL(window, OnComposeChange(std::u16string(u"nihao"), 0)).Times(0);
87  EXPECT_CALL(window, OnComposeChange(std::u16string(u"`}"), 0)).Times(1);
88  EXPECT_CALL(window, OnComposeCommit()).Times(1);
89  ON_CALL(window, OnImeComposition)
90  .WillByDefault(Invoke(&window, &MockWindow::CallOnImeComposition));
91  EXPECT_CALL(window, OnImeComposition(_, _, _)).Times(1);
92 
93  // Send an IME_COMPOSITION event that contains just the result string.
94  window.InjectWindowMessage(WM_IME_COMPOSITION, 0, GCS_RESULTSTR);
95 }
96 
97 TEST(MockWindow, OnImeCompositionResultAndCompose) {
98  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
99  auto* text_input_manager = new MockTextInputManager();
100  std::unique_ptr<TextInputManager> text_input_manager_ptr(text_input_manager);
101  MockWindow window(std::move(windows_proc_table),
102  std::move(text_input_manager_ptr));
103 
104  // This situation is that Google Japanese Input finished composing "今日" in
105  // "今日は" but is still composing "は".
106  {
107  InSequence dummy;
108  EXPECT_CALL(*text_input_manager, GetResultString())
109  .WillRepeatedly(
110  Return(std::optional<std::u16string>(std::u16string(u"今日"))));
111  EXPECT_CALL(*text_input_manager, GetComposingString())
112  .WillRepeatedly(
113  Return(std::optional<std::u16string>(std::u16string(u"は"))));
114  }
115  {
116  InSequence dummy;
117  EXPECT_CALL(window, OnComposeChange(std::u16string(u"今日"), 0)).Times(1);
118  EXPECT_CALL(window, OnComposeCommit()).Times(1);
119  EXPECT_CALL(window, OnComposeChange(std::u16string(u"は"), 0)).Times(1);
120  }
121 
122  EXPECT_CALL(*text_input_manager, GetComposingCursorPosition())
123  .WillRepeatedly(Return((int)0));
124 
125  ON_CALL(window, OnImeComposition)
126  .WillByDefault(Invoke(&window, &MockWindow::CallOnImeComposition));
127  EXPECT_CALL(window, OnImeComposition(_, _, _)).Times(1);
128 
129  // send an IME_COMPOSITION event that contains both the result string and the
130  // composition string.
131  window.InjectWindowMessage(WM_IME_COMPOSITION, 0,
132  GCS_COMPSTR | GCS_RESULTSTR);
133 }
134 
135 TEST(MockWindow, OnImeCompositionClearChange) {
136  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
137  auto* text_input_manager = new MockTextInputManager();
138  std::unique_ptr<TextInputManager> text_input_manager_ptr(text_input_manager);
139  MockWindow window(std::move(windows_proc_table),
140  std::move(text_input_manager_ptr));
141  EXPECT_CALL(window, OnComposeChange(std::u16string(u""), 0)).Times(1);
142  EXPECT_CALL(window, OnComposeCommit()).Times(1);
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, 0);
150 }
151 
152 TEST(MockWindow, HorizontalScroll) {
153  MockWindow window;
154  const int scroll_amount = 10;
155  // Vertical scroll should be passed along, adjusted for scroll tick size.
156  EXPECT_CALL(window, OnScroll(scroll_amount / 120.0, 0,
157  kFlutterPointerDeviceKindMouse, 0))
158  .Times(1);
159 
160  window.InjectWindowMessage(WM_MOUSEHWHEEL, MAKEWPARAM(0, scroll_amount), 0);
161 }
162 
163 TEST(MockWindow, MouseLeave) {
164  MockWindow window;
165  const double mouse_x = 10.0;
166  const double mouse_y = 20.0;
167 
168  EXPECT_CALL(window, OnPointerMove(mouse_x, mouse_y,
169  kFlutterPointerDeviceKindMouse, 0, 0))
170  .Times(1);
171  EXPECT_CALL(window, OnPointerLeave(mouse_x, mouse_y,
172  kFlutterPointerDeviceKindMouse, 0))
173  .Times(1);
174 
175  window.InjectWindowMessage(WM_MOUSEMOVE, 0, MAKELPARAM(mouse_x, mouse_y));
176  window.InjectWindowMessage(WM_MOUSELEAVE, 0, 0);
177 }
178 
179 TEST(MockWindow, KeyDown) {
180  MockWindow window;
181  EXPECT_CALL(window, OnKey(_, _, _, _, _, _, _)).Times(1);
182  LPARAM lparam = CreateKeyEventLparam(42, false, false);
183  // send a "Shift" key down event.
184  window.InjectWindowMessage(WM_KEYDOWN, 16, lparam);
185 }
186 
187 TEST(MockWindow, KeyUp) {
188  MockWindow window;
189  EXPECT_CALL(window, OnKey(_, _, _, _, _, _, _)).Times(1);
190  LPARAM lparam = CreateKeyEventLparam(42, false, true);
191  // send a "Shift" key up event.
192  window.InjectWindowMessage(WM_KEYUP, 16, lparam);
193 }
194 
195 TEST(MockWindow, SysKeyDown) {
196  MockWindow window;
197  EXPECT_CALL(window, OnKey(_, _, _, _, _, _, _)).Times(1);
198  LPARAM lparam = CreateKeyEventLparam(42, false, false);
199  // send a "Shift" key down event.
200  window.InjectWindowMessage(WM_SYSKEYDOWN, 16, lparam);
201 }
202 
203 TEST(MockWindow, SysKeyUp) {
204  MockWindow window;
205  EXPECT_CALL(window, OnKey(_, _, _, _, _, _, _)).Times(1);
206  LPARAM lparam = CreateKeyEventLparam(42, false, true);
207  // send a "Shift" key up event.
208  window.InjectWindowMessage(WM_SYSKEYUP, 16, lparam);
209 }
210 
211 TEST(MockWindow, KeyDownPrintable) {
212  MockWindow window;
213  LPARAM lparam = CreateKeyEventLparam(30, false, false);
214 
215  auto respond_false = [](int key, int scancode, int action, char32_t character,
216  bool extended, bool was_down,
217  std::function<void(bool)> callback) {
218  callback(false);
219  };
220  EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 0, false, false, _))
221  .Times(1)
222  .WillOnce(respond_false);
223  EXPECT_CALL(window, OnText(_)).Times(1);
224  std::array<Win32Message, 2> messages = {
225  Win32Message{WM_KEYDOWN, 65, lparam, kWmResultDontCheck},
226  Win32Message{WM_CHAR, 65, lparam, kWmResultDontCheck}};
227  window.InjectMessageList(2, messages.data());
228 }
229 
230 TEST(MockWindow, KeyDownWithCtrl) {
231  MockWindow window;
232 
233  // Simulate CONTROL pressed
234  std::array<BYTE, 256> keyboard_state;
235  keyboard_state[VK_CONTROL] = -1;
236  SetKeyboardState(keyboard_state.data());
237 
238  LPARAM lparam = CreateKeyEventLparam(30, false, false);
239 
240  // Expect OnKey, but not OnText, because Control + Key is not followed by
241  // WM_CHAR
242  EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 0, false, false, _)).Times(1);
243  EXPECT_CALL(window, OnText(_)).Times(0);
244 
245  window.InjectWindowMessage(WM_KEYDOWN, 65, lparam);
246 
247  keyboard_state.fill(0);
248  SetKeyboardState(keyboard_state.data());
249 }
250 
251 TEST(MockWindow, KeyDownWithCtrlToggled) {
252  MockWindow window;
253 
254  auto respond_false = [](int key, int scancode, int action, char32_t character,
255  bool extended, bool was_down,
256  std::function<void(bool)> callback) {
257  callback(false);
258  };
259 
260  // Simulate CONTROL toggled
261  std::array<BYTE, 256> keyboard_state;
262  keyboard_state[VK_CONTROL] = 1;
263  SetKeyboardState(keyboard_state.data());
264 
265  LPARAM lparam = CreateKeyEventLparam(30, false, false);
266 
267  EXPECT_CALL(window, OnKey(65, 30, WM_KEYDOWN, 0, false, false, _))
268  .Times(1)
269  .WillOnce(respond_false);
270  EXPECT_CALL(window, OnText(_)).Times(1);
271 
272  // send a "A" key down event.
273  Win32Message messages[] = {{WM_KEYDOWN, 65, lparam, kWmResultDontCheck},
274  {WM_CHAR, 65, lparam, kWmResultDontCheck}};
275  window.InjectMessageList(2, messages);
276 
277  keyboard_state.fill(0);
278  SetKeyboardState(keyboard_state.data());
279 }
280 
281 TEST(MockWindow, Paint) {
282  MockWindow window;
283  EXPECT_CALL(window, OnPaint()).Times(1);
284  window.InjectWindowMessage(WM_PAINT, 0, 0);
285 }
286 
287 // Verify direct manipulation isn't notified of pointer hit tests.
288 TEST(MockWindow, PointerHitTest) {
289  UINT32 pointer_id = 123;
290  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
291  auto text_input_manager = std::make_unique<MockTextInputManager>();
292 
293  EXPECT_CALL(*windows_proc_table, GetPointerType(Eq(pointer_id), _))
294  .Times(1)
295  .WillOnce([](UINT32 pointer_id, POINTER_INPUT_TYPE* type) {
296  *type = PT_POINTER;
297  return TRUE;
298  });
299 
300  MockWindow window(std::move(windows_proc_table),
301  std::move(text_input_manager));
302 
303  auto direct_manipulation =
304  std::make_unique<MockDirectManipulationOwner>(&window);
305 
306  EXPECT_CALL(*direct_manipulation, SetContact).Times(0);
307 
308  window.SetDirectManipulationOwner(std::move(direct_manipulation));
309  window.InjectWindowMessage(DM_POINTERHITTEST, MAKEWPARAM(pointer_id, 0), 0);
310 }
311 
312 // Verify direct manipulation is notified of touchpad hit tests.
313 TEST(MockWindow, TouchPadHitTest) {
314  UINT32 pointer_id = 123;
315  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
316  auto text_input_manager = std::make_unique<MockTextInputManager>();
317 
318  EXPECT_CALL(*windows_proc_table, GetPointerType(Eq(pointer_id), _))
319  .Times(1)
320  .WillOnce([](UINT32 pointer_id, POINTER_INPUT_TYPE* type) {
321  *type = PT_TOUCHPAD;
322  return TRUE;
323  });
324 
325  MockWindow window(std::move(windows_proc_table),
326  std::move(text_input_manager));
327 
328  auto direct_manipulation =
329  std::make_unique<MockDirectManipulationOwner>(&window);
330 
331  EXPECT_CALL(*direct_manipulation, SetContact(Eq(pointer_id))).Times(1);
332 
333  window.SetDirectManipulationOwner(std::move(direct_manipulation));
334  window.InjectWindowMessage(DM_POINTERHITTEST, MAKEWPARAM(pointer_id, 0), 0);
335 }
336 
337 // Verify direct manipulation isn't notified of unknown hit tests.
338 // This can happen if determining the pointer type fails, for example,
339 // if GetPointerType is unsupported by the current Windows version.
340 // See: https://github.com/flutter/flutter/issues/109412
341 TEST(MockWindow, UnknownPointerTypeSkipsDirectManipulation) {
342  UINT32 pointer_id = 123;
343  auto windows_proc_table = std::make_unique<MockWindowsProcTable>();
344  auto text_input_manager = std::make_unique<MockTextInputManager>();
345 
346  EXPECT_CALL(*windows_proc_table, GetPointerType(Eq(pointer_id), _))
347  .Times(1)
348  .WillOnce(
349  [](UINT32 pointer_id, POINTER_INPUT_TYPE* type) { return FALSE; });
350 
351  MockWindow window(std::move(windows_proc_table),
352  std::move(text_input_manager));
353 
354  auto direct_manipulation =
355  std::make_unique<MockDirectManipulationOwner>(&window);
356 
357  EXPECT_CALL(*direct_manipulation, SetContact).Times(0);
358 
359  window.SetDirectManipulationOwner(std::move(direct_manipulation));
360  window.InjectWindowMessage(DM_POINTERHITTEST, MAKEWPARAM(pointer_id, 0), 0);
361 }
362 
363 // Test that the root UIA object is queried by WM_GETOBJECT.
364 TEST(MockWindow, DISABLED_GetObjectUia) {
365  MockWindow window;
366  bool uia_called = false;
367  ON_CALL(window, OnGetObject)
368  .WillByDefault(Invoke([&uia_called](UINT msg, WPARAM wpar, LPARAM lpar) {
369 #ifdef FLUTTER_ENGINE_USE_UIA
370  uia_called = true;
371 #endif // FLUTTER_ENGINE_USE_UIA
372  return static_cast<LRESULT>(0);
373  }));
374  EXPECT_CALL(window, OnGetObject).Times(1);
375 
376  window.InjectWindowMessage(WM_GETOBJECT, 0, UiaRootObjectId);
377 
378  EXPECT_TRUE(uia_called);
379 }
380 
381 } // namespace testing
382 } // namespace flutter
scancode
int scancode
Definition: keyboard_key_handler_unittests.cc:115
was_down
bool was_down
Definition: keyboard_key_handler_unittests.cc:119
extended
bool extended
Definition: keyboard_key_handler_unittests.cc:118
character
char32_t character
Definition: keyboard_key_handler_unittests.cc:117
type
enum flutter::testing::@87::KeyboardChange::Type type
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::testing::TEST
TEST(AccessibilityBridgeWindows, GetParent)
Definition: accessibility_bridge_windows_unittests.cc:237
action
int action
Definition: keyboard_key_handler_unittests.cc:116
key
int key
Definition: keyboard_key_handler_unittests.cc:114
callback
FlutterDesktopBinaryReply callback
Definition: flutter_windows_view_unittests.cc:51