| Flutter Windows Embedder |

keyboard_key_handler_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 #include "flutter/shell/platform/windows/keyboard_key_handler.h"
5
6 #include <rapidjson/document.h>
7 #include <map>
8 #include <memory>
9 #include "flutter/shell/platform/common/client_wrapper/include/flutter/method_result_functions.h"
10 #include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_message_codec.h"
11 #include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_method_codec.h"
12 #include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
13 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
14 #include "flutter/shell/platform/windows/keyboard_utils.h"
15 #include "flutter/shell/platform/windows/testing/engine_modifier.h"
16 #include "flutter/shell/platform/windows/testing/test_binary_messenger.h"
17
18 #include "flutter/fml/macros.h"
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21
22 namespace flutter {
23 namespace testing {
24
25 namespace {
26
27 static constexpr char kChannelName[] = "flutter/keyboard";
28 static constexpr char kGetKeyboardStateMethod[] = "getKeyboardState";
29
30 constexpr SHORT kStateMaskToggled = 0x01;
31 constexpr SHORT kStateMaskPressed = 0x80;
32
33 class TestFlutterKeyEvent : public FlutterKeyEvent {
34 public:
35 TestFlutterKeyEvent(const FlutterKeyEvent& src,
36 FlutterKeyEventCallback callback,
37 void* user_data)
38 : character_str(src.character), callback(callback), user_data(user_data) {
39 struct_size = src.struct_size;
40 timestamp = src.timestamp;
41 type = src.type;
42 physical = src.physical;
43 logical = src.logical;
44 character = character_str.c_str();
45 synthesized = src.synthesized;
46 }
47
48 TestFlutterKeyEvent(TestFlutterKeyEvent&& source)
49 : FlutterKeyEvent(source),
50 callback(std::move(source.callback)),
51 user_data(source.user_data) {
52 character = character_str.c_str();
53 }
54
55 FlutterKeyEventCallback callback;
56 void* user_data;
57
58 private:
59 const std::string character_str;
60 };
61
62 class TestKeystate {
63 public:
64 void Set(int virtual_key, bool pressed, bool toggled_on = false) {
65 state_[virtual_key] = (pressed ? kStateMaskPressed : 0) |
66 (toggled_on ? kStateMaskToggled : 0);
67 }
68
69 SHORT Get(int virtual_key) { return state_[virtual_key]; }
70
71 KeyboardKeyEmbedderHandler::GetKeyStateHandler Getter() {
72 return [this](int virtual_key) { return Get(virtual_key); };
73 }
74
75 private:
76 std::map<int, SHORT> state_;
77 };
78
79 UINT DefaultMapVkToScan(UINT virtual_key, bool extended) {
80 return MapVirtualKey(virtual_key,
81 extended ? MAPVK_VK_TO_VSC_EX : MAPVK_VK_TO_VSC);
82 }
83
84 static constexpr int kHandledScanCode = 20;
85 static constexpr int kHandledScanCode2 = 22;
86 static constexpr int kUnhandledScanCode = 21;
87
88 constexpr uint64_t kScanCodeShiftRight = 0x36;
89 constexpr uint64_t kScanCodeControl = 0x1D;
90 constexpr uint64_t kScanCodeAltLeft = 0x38;
91
92 constexpr uint64_t kScanCodeKeyA = 0x1e;
93 constexpr uint64_t kVirtualKeyA = 0x41;
94
95 typedef std::function<void(bool)> Callback;
96 typedef std::function<void(Callback&)> CallbackHandler;
97 void dont_respond(Callback& callback) {}
98 void respond_true(Callback& callback) {
99 callback(true);
100 }
101 void respond_false(Callback& callback) {
102 callback(false);
103 }
104
105 // A testing |KeyHandlerDelegate| that records all calls
106 // to |KeyboardHook| and can be customized with whether
107 // the hook is handled in async.
108 class MockKeyHandlerDelegate
109 : public KeyboardKeyHandler::KeyboardKeyHandlerDelegate {
110 public:
111 class KeyboardHookCall {
112 public:
113 int delegate_id;
114 int key;
115 int scancode;
116 int action;
117 char32_t character;
118 bool extended;
119 bool was_down;
120 std::function<void(bool)> callback;
121 };
122
123 // Create a |MockKeyHandlerDelegate|.
124 //
125 // The |delegate_id| is an arbitrary ID to tell between delegates
126 // that will be recorded in |KeyboardHookCall|.
127 //
128 // The |hook_history| will store every call to |KeyboardHookCall| that are
129 // handled asynchronously.
130 //
131 // The |is_async| is a function that the class calls upon every
132 // |KeyboardHookCall| to decide whether the call is handled asynchronously.
133 // Defaults to always returning true (async).
134 MockKeyHandlerDelegate(int delegate_id,
135 std::list<KeyboardHookCall>* hook_history)
136 : delegate_id(delegate_id),
137 hook_history(hook_history),
138 callback_handler(dont_respond) {}
139 virtual ~MockKeyHandlerDelegate() = default;
140
141 virtual void KeyboardHook(int key,
142 int scancode,
143 int action,
144 char32_t character,
145 bool extended,
146 bool was_down,
147 std::function<void(bool)> callback) {
148 hook_history->push_back(KeyboardHookCall{
149 .delegate_id = delegate_id,
150 .key = key,
151 .scancode = scancode,
152 .character = character,
153 .extended = extended,
154 .was_down = was_down,
155 .callback = std::move(callback),
156 });
157 callback_handler(hook_history->back().callback);
158 }
159
160 virtual void SyncModifiersIfNeeded(int modifiers_state) {
161 // Do Nothing
162 }
163
164 virtual std::map<uint64_t, uint64_t> GetPressedState() {
165 std::map<uint64_t, uint64_t> Empty_State;
166 return Empty_State;
167 }
168
169 CallbackHandler callback_handler;
170 int delegate_id;
171 std::list<KeyboardHookCall>* hook_history;
172
173 private:
174 FML_DISALLOW_COPY_AND_ASSIGN(MockKeyHandlerDelegate);
175 };
176
177 enum KeyEventResponse {
178 kNoResponse,
179 kHandled,
180 kUnhandled,
181 };
182
183 static KeyEventResponse key_event_response = kNoResponse;
184
185 void OnKeyEventResult(bool handled) {
186 key_event_response = handled ? kHandled : kUnhandled;
187 }
188
189 void SimulateKeyboardMessage(TestBinaryMessenger* messenger,
190 const std::string& method_name,
191 std::unique_ptr<EncodableValue> arguments,
192 MethodResult<EncodableValue>* result_handler) {
193 MethodCall<> call(method_name, std::move(arguments));
194 auto message = StandardMethodCodec::GetInstance().EncodeMethodCall(call);
195
196 EXPECT_TRUE(messenger->SimulateEngineMessage(
197 kChannelName, message->data(), message->size(),
198 [&result_handler](const uint8_t* reply, size_t reply_size) {
199 StandardMethodCodec::GetInstance().DecodeAndProcessResponseEnvelope(
200 reply, reply_size, result_handler);
201 }));
202 }
203
204 } // namespace
205
206 using namespace ::flutter::testing::keycodes;
207
208 TEST(KeyboardKeyHandlerTest, SingleDelegateWithAsyncResponds) {
209 std::list<MockKeyHandlerDelegate::KeyboardHookCall> hook_history;
210
211 TestBinaryMessenger messenger([](const std::string& channel,
212 const uint8_t* message, size_t message_size,
213 BinaryReply reply) {});
214 KeyboardKeyHandler handler(&messenger);
215
216 // Add one delegate
217 auto delegate = std::make_unique<MockKeyHandlerDelegate>(1, &hook_history);
218 handler.AddDelegate(std::move(delegate));
219
220 /// Test 1: One event that is handled by the framework
221
222 // Dispatch a key event
223 handler.KeyboardHook(64, kHandledScanCode, WM_KEYDOWN, L'a', false, true,
224 OnKeyEventResult);
225 EXPECT_EQ(key_event_response, kNoResponse);
226 EXPECT_EQ(hook_history.size(), 1);
227 EXPECT_EQ(hook_history.back().delegate_id, 1);
228 EXPECT_EQ(hook_history.back().scancode, kHandledScanCode);
229 EXPECT_EQ(hook_history.back().was_down, true);
230
231 EXPECT_EQ(key_event_response, kNoResponse);
232 hook_history.back().callback(true);
233 EXPECT_EQ(key_event_response, kHandled);
234
235 key_event_response = kNoResponse;
236 hook_history.clear();
237
238 /// Test 2: Two events that are unhandled by the framework
239
240 handler.KeyboardHook(64, kHandledScanCode, WM_KEYDOWN, L'a', false, false,
241 OnKeyEventResult);
242 EXPECT_EQ(key_event_response, kNoResponse);
243 EXPECT_EQ(hook_history.size(), 1);
244 EXPECT_EQ(hook_history.back().delegate_id, 1);
245 EXPECT_EQ(hook_history.back().scancode, kHandledScanCode);
246 EXPECT_EQ(hook_history.back().was_down, false);
247
248 // Dispatch another key event
249 handler.KeyboardHook(65, kHandledScanCode2, WM_KEYUP, L'b', false, true,
250 OnKeyEventResult);
251 EXPECT_EQ(key_event_response, kNoResponse);
252 EXPECT_EQ(hook_history.size(), 2);
253 EXPECT_EQ(hook_history.back().delegate_id, 1);
254 EXPECT_EQ(hook_history.back().scancode, kHandledScanCode2);
255 EXPECT_EQ(hook_history.back().was_down, true);
256
257 // Resolve the second event first to test out-of-order response
258 hook_history.back().callback(false);
259 EXPECT_EQ(key_event_response, kUnhandled);
260 key_event_response = kNoResponse;
261
262 // Resolve the first event then
263 hook_history.front().callback(false);
264 EXPECT_EQ(key_event_response, kUnhandled);
265
266 hook_history.clear();
267 key_event_response = kNoResponse;
268 }
269
270 TEST(KeyboardKeyHandlerTest, SingleDelegateWithSyncResponds) {
271 std::list<MockKeyHandlerDelegate::KeyboardHookCall> hook_history;
272
273 TestBinaryMessenger messenger([](const std::string& channel,
274 const uint8_t* message, size_t message_size,
275 BinaryReply reply) {});
276 KeyboardKeyHandler handler(&messenger);
277 // Add one delegate
278 auto delegate = std::make_unique<MockKeyHandlerDelegate>(1, &hook_history);
279 CallbackHandler& delegate_handler = delegate->callback_handler;
280 handler.AddDelegate(std::move(delegate));
281
282 /// Test 1: One event that is handled by the framework
283
284 // Dispatch a key event
285 delegate_handler = respond_true;
286 handler.KeyboardHook(64, kHandledScanCode, WM_KEYDOWN, L'a', false, false,
287 OnKeyEventResult);
288 EXPECT_EQ(key_event_response, kHandled);
289 EXPECT_EQ(hook_history.size(), 1);
290 EXPECT_EQ(hook_history.back().delegate_id, 1);
291 EXPECT_EQ(hook_history.back().scancode, kHandledScanCode);
292 EXPECT_EQ(hook_history.back().was_down, false);
293
294 hook_history.clear();
295
296 /// Test 2: An event unhandled by the framework
297
298 delegate_handler = respond_false;
299 handler.KeyboardHook(64, kHandledScanCode, WM_KEYDOWN, L'a', false, false,
300 OnKeyEventResult);
301 EXPECT_EQ(key_event_response, kUnhandled);
302 EXPECT_EQ(hook_history.size(), 1);
303 EXPECT_EQ(hook_history.back().delegate_id, 1);
304 EXPECT_EQ(hook_history.back().scancode, kHandledScanCode);
305 EXPECT_EQ(hook_history.back().was_down, false);
306
307 hook_history.clear();
308 key_event_response = kNoResponse;
309 }
310
311 TEST(KeyboardKeyHandlerTest, HandlerGetPressedState) {
312 TestKeystate key_state;
313
314 TestBinaryMessenger messenger([](const std::string& channel,
315 const uint8_t* message, size_t message_size,
316 BinaryReply reply) {});
317 KeyboardKeyHandler handler(&messenger);
318
319 std::unique_ptr<KeyboardKeyEmbedderHandler> embedder_handler =
320 std::make_unique<KeyboardKeyEmbedderHandler>(
321 [](const FlutterKeyEvent& event, FlutterKeyEventCallback callback,
322 void* user_data) {},
323 key_state.Getter(), DefaultMapVkToScan);
324 handler.AddDelegate(std::move(embedder_handler));
325
326 // Dispatch a key event.
327 handler.KeyboardHook(kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false,
328 false, OnKeyEventResult);
329
330 std::map<uint64_t, uint64_t> pressed_state = handler.GetPressedState();
331 EXPECT_EQ(pressed_state.size(), 1);
332 EXPECT_EQ(pressed_state.at(kPhysicalKeyA), kLogicalKeyA);
333 }
334
335 TEST(KeyboardKeyHandlerTest, KeyboardChannelGetPressedState) {
336 TestKeystate key_state;
337 TestBinaryMessenger messenger;
338 KeyboardKeyHandler handler(&messenger);
339
340 std::unique_ptr<KeyboardKeyEmbedderHandler> embedder_handler =
341 std::make_unique<KeyboardKeyEmbedderHandler>(
342 [](const FlutterKeyEvent& event, FlutterKeyEventCallback callback,
343 void* user_data) {},
344 key_state.Getter(), DefaultMapVkToScan);
345 handler.AddDelegate(std::move(embedder_handler));
346 handler.InitKeyboardChannel();
347
348 // Dispatch a key event.
349 handler.KeyboardHook(kVirtualKeyA, kScanCodeKeyA, WM_KEYDOWN, 'a', false,
350 false, OnKeyEventResult);
351
352 bool success = false;
353
354 MethodResultFunctions<> result_handler(
355 [&success](const EncodableValue* result) {
356 success = true;
357 auto& map = std::get<EncodableMap>(*result);
358 EXPECT_EQ(map.size(), 1);
359 EncodableValue physical_value(static_cast<long long>(kPhysicalKeyA));
360 EncodableValue logical_value(static_cast<long long>(kLogicalKeyA));
361 EXPECT_EQ(map.at(physical_value), logical_value);
362 },
363 nullptr, nullptr);
364
365 SimulateKeyboardMessage(&messenger, kGetKeyboardStateMethod, nullptr,
366 &result_handler);
367 EXPECT_TRUE(success);
368 }
369
370 } // namespace testing
371 } // namespace flutter
flutter::KeyboardKeyHandler
Definition: keyboard_key_handler.h:35
hook_history
std::list< KeyboardHookCall > * hook_history
Definition: keyboard_key_handler_unittests.cc:171
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
callback_handler
CallbackHandler callback_handler
Definition: keyboard_key_handler_unittests.cc:169
method_result_functions.h
flutter::KeyboardKeyEmbedderHandler::GetKeyStateHandler
std::function< SHORT(int)> GetKeyStateHandler
Definition: keyboard_key_embedder_handler.h:41
character
char32_t character
Definition: keyboard_key_handler_unittests.cc:117
standard_method_codec.h
user_data
void * user_data
Definition: keyboard_key_handler_unittests.cc:56
flutter::KeyboardKeyHandler::KeyboardHook
void KeyboardHook(int key, int scancode, int action, char32_t character, bool extended, bool was_down, KeyEventCallback callback) override
Definition: keyboard_key_handler.cc:83
toggled_on
bool toggled_on
Definition: keyboard_unittests.cc:127
flutter::KeyboardKeyHandler::InitKeyboardChannel
void InitKeyboardChannel()
Definition: keyboard_key_handler.cc:40
keyboard_utils.h
standard_message_codec.h
flutter::testing::kScanCodeKeyA
constexpr uint64_t kScanCodeKeyA
Definition: flutter_windows_view_unittests.cc:38
flutter::BinaryReply
std::function< void(const uint8_t *reply, size_t reply_size)> BinaryReply
Definition: binary_messenger.h:17
type
enum flutter::testing::@69::KeyboardChange::Type type
flutter::MethodCodec::EncodeMethodCall
std::unique_ptr< std::vector< uint8_t > > EncodeMethodCall(const MethodCall< T > &method_call) const
Definition: method_codec.h:48
flutter
Definition: accessibility_bridge_windows.cc:11
pressed
bool pressed
Definition: keyboard_unittests.cc:126
kChannelName
static constexpr char kChannelName[]
Definition: cursor_handler.cc:13
callback
FlutterKeyEventCallback callback
Definition: keyboard_key_handler_unittests.cc:55
flutter::EncodableValue
Definition: encodable_value.h:165
keyboard_key_handler.h
flutter::KeyboardKeyHandler::AddDelegate
void AddDelegate(std::unique_ptr< KeyboardKeyHandlerDelegate > delegate)
Definition: keyboard_key_handler.cc:66
flutter::testing::TEST
TEST(AccessibilityBridgeWindows, GetParent)
Definition: accessibility_bridge_windows_unittests.cc:235
delegate_id
int delegate_id
Definition: keyboard_key_handler_unittests.cc:113
message
Win32Message message
Definition: keyboard_unittests.cc:137
action
int action
Definition: keyboard_key_handler_unittests.cc:116
flutter::kScanCodeShiftRight
constexpr int kScanCodeShiftRight
Definition: keyboard_utils.h:17
key
int key
Definition: keyboard_key_handler_unittests.cc:114
flutter::testing::kVirtualKeyA
constexpr uint64_t kVirtualKeyA
Definition: flutter_windows_view_unittests.cc:39
flutter::StandardMethodCodec::GetInstance
static const StandardMethodCodec & GetInstance(const StandardCodecSerializer *serializer=nullptr)
Definition: standard_codec.cc:340
flutter::MethodResultFunctions
Definition: method_result_functions.h:31