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.
5 
6 #include <rapidjson/document.h>
7 #include <map>
8 #include <memory>
12 #include "flutter/shell/platform/embedder/test_utils/key_codes.g.h"
13 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.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 
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:
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)
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));
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:42
flutter::BinaryReply
std::function< void(const uint8_t *reply, size_t reply_size)> BinaryReply
Definition: binary_messenger.h:17
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
type
enum flutter::testing::@88::KeyboardChange::Type type
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:237
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:43
flutter::StandardMethodCodec::GetInstance
static const StandardMethodCodec & GetInstance(const StandardCodecSerializer *serializer=nullptr)
Definition: standard_codec.cc:340
flutter::MethodResultFunctions
Definition: method_result_functions.h:31