Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
11#include "gmock/gmock.h"
12#include "gtest/gtest.h"
13
14using testing::_;
15using testing::Eq;
16using testing::InSequence;
17using testing::Invoke;
18using testing::Return;
19
20namespace flutter {
21namespace testing {
22
23TEST(MockWindow, CreateDestroy) {
25 ASSERT_TRUE(TRUE);
26}
27
28TEST(MockWindow, GetDpiAfterCreate) {
30 ASSERT_TRUE(window.GetDpi() > 0);
31}
32
33TEST(MockWindow, VerticalScroll) {
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,
40 .Times(1);
41
42 window.InjectWindowMessage(WM_MOUSEWHEEL, MAKEWPARAM(0, scroll_amount), 0);
43}
44
45TEST(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.
71TEST(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
91TEST(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
116TEST(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
153TEST(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
175TEST(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
197TEST(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
214TEST(MockWindow, HorizontalScroll) {
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,
220 .Times(1);
221
222 window.InjectWindowMessage(WM_MOUSEHWHEEL, MAKEWPARAM(0, scroll_amount), 0);
223}
224
225TEST(MockWindow, MouseLeave) {
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,
236 .Times(1);
237
238 window.InjectWindowMessage(WM_MOUSEMOVE, 0, MAKELPARAM(mouse_x, mouse_y));
239 window.InjectWindowMessage(WM_MOUSELEAVE, 0, 0);
240}
241
242TEST(MockWindow, KeyDown) {
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
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
258TEST(MockWindow, SysKeyDown) {
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
266TEST(MockWindow, SysKeyUp) {
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
277TEST(MockWindow, KeyDownPrintable) {
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
296TEST(MockWindow, KeyDownWithCtrl) {
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
317TEST(MockWindow, KeyDownWithCtrlToggled) {
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
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.
354TEST(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.
379TEST(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
407TEST(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.
430TEST(MockWindow, DISABLED_GetObjectUia) {
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
Mock for the |Window| base class.
Mock for the |FlutterWindow| base class.
Definition mock_window.h:17
LRESULT InjectWindowMessage(UINT const message, WPARAM const wparam, LPARAM const lparam)
void CallOnImeComposition(UINT const message, WPARAM const wparam, LPARAM const lparam)
@ kFlutterPointerDeviceKindMouse
Definition embedder.h:1310
GLFWwindow * window
Definition main.cc:60
return TRUE
FlutterDesktopBinaryReply callback
TEST(NativeAssetsManagerTest, NoAvailableAssets)
constexpr LRESULT kWmResultDontCheck
Definition wm_builders.h:16
LPARAM CreateKeyEventLparam(USHORT scancode, bool extended, bool was_down, USHORT repeat_count, bool context_code, bool transition_state)
impeller::ShaderType type
LONG_PTR LRESULT
unsigned int UINT
LONG_PTR LPARAM
UINT_PTR WPARAM