Flutter Linux Embedder
fl_text_input_handler_test.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 <utility>
6 
12 #include "flutter/shell/platform/linux/testing/fl_test.h"
13 #include "flutter/shell/platform/linux/testing/mock_binary_messenger.h"
14 #include "flutter/shell/platform/linux/testing/mock_binary_messenger_response_handle.h"
15 #include "flutter/shell/platform/linux/testing/mock_im_context.h"
16 #include "flutter/shell/platform/linux/testing/mock_text_input_view_delegate.h"
17 #include "flutter/testing/testing.h"
18 
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21 
22 void printTo(FlMethodResponse* response, ::std::ostream* os) {
23  *os << ::testing::PrintToString(
24  fl_method_response_get_result(response, nullptr));
25 }
26 
27 MATCHER_P(SuccessResponse, result, "") {
28  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
29  g_autoptr(FlMethodResponse) response =
30  fl_method_codec_decode_response(FL_METHOD_CODEC(codec), arg, nullptr);
31  if (fl_value_equal(fl_method_response_get_result(response, nullptr),
32  result)) {
33  return true;
34  }
35  *result_listener << ::testing::PrintToString(response);
36  return false;
37 }
38 
39 MATCHER_P(FlValueEq, value, "equal to " + ::testing::PrintToString(value)) {
40  return fl_value_equal(arg, value);
41 }
42 
43 class MethodCallMatcher {
44  public:
45  using is_gtest_matcher = void;
46 
47  explicit MethodCallMatcher(::testing::Matcher<std::string> name,
48  ::testing::Matcher<FlValue*> args)
49  : name_(std::move(name)), args_(std::move(args)) {}
50 
52  ::testing::MatchResultListener* result_listener) const {
53  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
54  g_autoptr(GError) error = nullptr;
55  g_autofree gchar* name = nullptr;
56  g_autoptr(FlValue) args = nullptr;
58  FL_METHOD_CODEC(codec), method_call, &name, &args, &error);
59  if (!result) {
60  *result_listener << ::testing::PrintToString(error->message);
61  return false;
62  }
63  if (!name_.MatchAndExplain(name, result_listener)) {
64  *result_listener << " where the name doesn't match: \"" << name << "\"";
65  return false;
66  }
67  if (!args_.MatchAndExplain(args, result_listener)) {
68  *result_listener << " where the args don't match: "
69  << ::testing::PrintToString(args);
70  return false;
71  }
72  return true;
73  }
74 
75  void DescribeTo(std::ostream* os) const {
76  *os << "method name ";
77  name_.DescribeTo(os);
78  *os << " and args ";
79  args_.DescribeTo(os);
80  }
81 
82  void DescribeNegationTo(std::ostream* os) const {
83  *os << "method name ";
84  name_.DescribeNegationTo(os);
85  *os << " or args ";
86  args_.DescribeNegationTo(os);
87  }
88 
89  private:
90  ::testing::Matcher<std::string> name_;
91  ::testing::Matcher<FlValue*> args_;
92 };
93 
94 ::testing::Matcher<GBytes*> MethodCall(const std::string& name,
95  ::testing::Matcher<FlValue*> args) {
96  return MethodCallMatcher(::testing::StrEq(name), std::move(args));
97 }
98 
99 static FlValue* build_map(std::map<const gchar*, FlValue*> args) {
101  for (auto it = args.begin(); it != args.end(); ++it) {
102  fl_value_set_string_take(value, it->first, it->second);
103  }
104  return value;
105 }
106 
107 static FlValue* build_list(std::vector<FlValue*> args) {
109  for (auto it = args.begin(); it != args.end(); ++it) {
111  }
112  return value;
113 }
114 
115 struct InputConfig {
116  int64_t client_id = -1;
117  const gchar* input_type = "TextInputType.text";
118  const gchar* input_action = "TextInputAction.none";
119  gboolean enable_delta_model = false;
120 };
121 
123  return build_list({
124  fl_value_new_int(config.client_id),
125  build_map({
126  {"inputAction", fl_value_new_string(config.input_action)},
127  {"inputType", build_map({
128  {"name", fl_value_new_string(config.input_type)},
129  })},
130  {"enableDeltaModel", fl_value_new_bool(config.enable_delta_model)},
131  }),
132  });
133 }
134 
135 struct EditingState {
136  const gchar* text = "";
137  int selection_base = -1;
139  int composing_base = -1;
141 };
142 
144  return build_map({
145  {"text", fl_value_new_string(state.text)},
146  {"selectionBase", fl_value_new_int(state.selection_base)},
147  {"selectionExtent", fl_value_new_int(state.selection_extent)},
148  {"selectionAffinity", fl_value_new_string("TextAffinity.downstream")},
149  {"selectionIsDirectional", fl_value_new_bool(false)},
150  {"composingBase", fl_value_new_int(state.composing_base)},
151  {"composingExtent", fl_value_new_int(state.composing_extent)},
152  });
153 }
154 
155 struct EditingDelta {
156  const gchar* old_text = "";
157  const gchar* delta_text = "";
158  int delta_start = -1;
159  int delta_end = -1;
160  int selection_base = -1;
162  int composing_base = -1;
164 };
165 
167  return build_map({
168  {"oldText", fl_value_new_string(delta.old_text)},
169  {"deltaText", fl_value_new_string(delta.delta_text)},
170  {"deltaStart", fl_value_new_int(delta.delta_start)},
171  {"deltaEnd", fl_value_new_int(delta.delta_end)},
172  {"selectionBase", fl_value_new_int(delta.selection_base)},
173  {"selectionExtent", fl_value_new_int(delta.selection_extent)},
174  {"selectionAffinity", fl_value_new_string("TextAffinity.downstream")},
175  {"selectionIsDirectional", fl_value_new_bool(false)},
176  {"composingBase", fl_value_new_int(delta.composing_base)},
177  {"composingExtent", fl_value_new_int(delta.composing_extent)},
178  });
179 }
180 
181 static void send_key_event(FlTextInputHandler* handler,
182  gint keyval,
183  gint state = 0) {
184  GdkEvent* gdk_event = gdk_event_new(GDK_KEY_PRESS);
185  gdk_event->key.keyval = keyval;
186  gdk_event->key.state = state;
187  g_autoptr(FlKeyEvent) key_event = fl_key_event_new_from_gdk_event(gdk_event);
188  fl_text_input_handler_filter_keypress(handler, key_event);
189 }
190 
191 TEST(FlTextInputHandlerTest, MessageHandler) {
192  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
193  ::testing::NiceMock<flutter::testing::MockIMContext> context;
194  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
195 
196  g_autoptr(FlTextInputHandler) handler =
197  fl_text_input_handler_new(messenger, context, delegate);
198  EXPECT_NE(handler, nullptr);
199 
200  EXPECT_TRUE(messenger.HasMessageHandler("flutter/textinput"));
201 }
202 
203 TEST(FlTextInputHandlerTest, SetClient) {
204  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
205  ::testing::NiceMock<flutter::testing::MockIMContext> context;
206  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
207 
208  g_autoptr(FlTextInputHandler) handler =
209  fl_text_input_handler_new(messenger, context, delegate);
210  EXPECT_NE(handler, nullptr);
211 
212  g_autoptr(FlValue) args = build_input_config({.client_id = 1});
213  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
214  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
215  FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr);
216 
217  g_autoptr(FlValue) null = fl_value_new_null();
218  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
219  ::testing::Eq<FlBinaryMessenger*>(messenger),
220  ::testing::_, SuccessResponse(null), ::testing::_))
221  .WillOnce(::testing::Return(true));
222 
223  messenger.ReceiveMessage("flutter/textinput", message);
224 }
225 
226 TEST(FlTextInputHandlerTest, Show) {
227  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
228  ::testing::NiceMock<flutter::testing::MockIMContext> context;
229  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
230 
231  g_autoptr(FlTextInputHandler) handler =
232  fl_text_input_handler_new(messenger, context, delegate);
233  EXPECT_NE(handler, nullptr);
234 
235  EXPECT_CALL(context,
236  gtk_im_context_focus_in(::testing::Eq<GtkIMContext*>(context)));
237 
238  g_autoptr(FlValue) null = fl_value_new_null();
239  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
240  ::testing::Eq<FlBinaryMessenger*>(messenger),
241  ::testing::_, SuccessResponse(null), ::testing::_))
242  .WillOnce(::testing::Return(true));
243 
244  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
245  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
246  FL_METHOD_CODEC(codec), "TextInput.show", nullptr, nullptr);
247 
248  messenger.ReceiveMessage("flutter/textinput", message);
249 }
250 
251 TEST(FlTextInputHandlerTest, Hide) {
252  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
253  ::testing::NiceMock<flutter::testing::MockIMContext> context;
254  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
255 
256  g_autoptr(FlTextInputHandler) handler =
257  fl_text_input_handler_new(messenger, context, delegate);
258  EXPECT_NE(handler, nullptr);
259 
260  EXPECT_CALL(context,
261  gtk_im_context_focus_out(::testing::Eq<GtkIMContext*>(context)));
262 
263  g_autoptr(FlValue) null = fl_value_new_null();
264  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
265  ::testing::Eq<FlBinaryMessenger*>(messenger),
266  ::testing::_, SuccessResponse(null), ::testing::_))
267  .WillOnce(::testing::Return(true));
268 
269  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
270  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
271  FL_METHOD_CODEC(codec), "TextInput.hide", nullptr, nullptr);
272 
273  messenger.ReceiveMessage("flutter/textinput", message);
274 }
275 
276 TEST(FlTextInputHandlerTest, ClearClient) {
277  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
278  ::testing::NiceMock<flutter::testing::MockIMContext> context;
279  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
280 
281  g_autoptr(FlTextInputHandler) handler =
282  fl_text_input_handler_new(messenger, context, delegate);
283  EXPECT_NE(handler, nullptr);
284 
285  g_autoptr(FlValue) null = fl_value_new_null();
286  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
287  ::testing::Eq<FlBinaryMessenger*>(messenger),
288  ::testing::_, SuccessResponse(null), ::testing::_))
289  .WillOnce(::testing::Return(true));
290 
291  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
292  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
293  FL_METHOD_CODEC(codec), "TextInput.clearClient", nullptr, nullptr);
294 
295  messenger.ReceiveMessage("flutter/textinput", message);
296 }
297 
298 TEST(FlTextInputHandlerTest, PerformAction) {
299  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
300  ::testing::NiceMock<flutter::testing::MockIMContext> context;
301  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
302 
303  g_autoptr(FlTextInputHandler) handler =
304  fl_text_input_handler_new(messenger, context, delegate);
305  EXPECT_NE(handler, nullptr);
306 
307  // set input config
308  g_autoptr(FlValue) config = build_input_config({
309  .client_id = 1,
310  .input_type = "TextInputType.multiline",
311  .input_action = "TextInputAction.newline",
312  });
313  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
315  FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
316 
317  g_autoptr(FlValue) null = fl_value_new_null();
318  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
319  ::testing::Eq<FlBinaryMessenger*>(messenger),
320  ::testing::_, SuccessResponse(null), ::testing::_))
321  .WillOnce(::testing::Return(true));
322 
323  messenger.ReceiveMessage("flutter/textinput", set_client);
324 
325  // set editing state
326  g_autoptr(FlValue) state = build_editing_state({
327  .text = "Flutter",
328  .selection_base = 7,
329  .selection_extent = 7,
330  });
331  g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
332  FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
333 
334  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
335  ::testing::Eq<FlBinaryMessenger*>(messenger),
336  ::testing::_, SuccessResponse(null), ::testing::_))
337  .WillOnce(::testing::Return(true));
338 
339  messenger.ReceiveMessage("flutter/textinput", set_state);
340 
341  // update editing state
342  g_autoptr(FlValue) new_state = build_list({
343  fl_value_new_int(1), // client_id
345  .text = "Flutter\n",
346  .selection_base = 8,
347  .selection_extent = 8,
348  }),
349  });
350 
351  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
352  ::testing::Eq<FlBinaryMessenger*>(messenger),
353  ::testing::StrEq("flutter/textinput"),
354  MethodCall("TextInputClient.updateEditingState",
355  FlValueEq(new_state)),
356  ::testing::_, ::testing::_, ::testing::_));
357 
358  // perform action
359  g_autoptr(FlValue) action = build_list({
360  fl_value_new_int(1), // client_id
361  fl_value_new_string("TextInputAction.newline"),
362  });
363 
364  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
365  ::testing::Eq<FlBinaryMessenger*>(messenger),
366  ::testing::StrEq("flutter/textinput"),
367  MethodCall("TextInputClient.performAction",
368  FlValueEq(action)),
369  ::testing::_, ::testing::_, ::testing::_));
370 
371  send_key_event(handler, GDK_KEY_Return);
372 }
373 
374 // Regression test for https://github.com/flutter/flutter/issues/125879.
375 TEST(FlTextInputHandlerTest, MultilineWithSendAction) {
376  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
377  ::testing::NiceMock<flutter::testing::MockIMContext> context;
378  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
379 
380  g_autoptr(FlTextInputHandler) handler =
381  fl_text_input_handler_new(messenger, context, delegate);
382  EXPECT_NE(handler, nullptr);
383 
384  // Set input config.
385  g_autoptr(FlValue) config = build_input_config({
386  .client_id = 1,
387  .input_type = "TextInputType.multiline",
388  .input_action = "TextInputAction.send",
389  });
390  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
392  FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
393 
394  g_autoptr(FlValue) null = fl_value_new_null();
395  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
396  ::testing::Eq<FlBinaryMessenger*>(messenger),
397  ::testing::_, SuccessResponse(null), ::testing::_))
398  .WillOnce(::testing::Return(true));
399 
400  messenger.ReceiveMessage("flutter/textinput", set_client);
401 
402  // Set editing state.
403  g_autoptr(FlValue) state = build_editing_state({
404  .text = "Flutter",
405  .selection_base = 7,
406  .selection_extent = 7,
407  });
408  g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
409  FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
410 
411  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
412  ::testing::Eq<FlBinaryMessenger*>(messenger),
413  ::testing::_, SuccessResponse(null), ::testing::_))
414  .WillOnce(::testing::Return(true));
415 
416  messenger.ReceiveMessage("flutter/textinput", set_state);
417 
418  // Perform action.
419  g_autoptr(FlValue) action = build_list({
420  fl_value_new_int(1), // client_id
421  fl_value_new_string("TextInputAction.send"),
422  });
423 
424  // Because the input action is not set to TextInputAction.newline, the next
425  // expected call is "TextInputClient.performAction". If the input action was
426  // set to TextInputAction.newline the next call would be
427  // "TextInputClient.updateEditingState" (this case is tested in the test named
428  // 'PerformAction').
429  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
430  ::testing::Eq<FlBinaryMessenger*>(messenger),
431  ::testing::StrEq("flutter/textinput"),
432  MethodCall("TextInputClient.performAction",
433  FlValueEq(action)),
434  ::testing::_, ::testing::_, ::testing::_));
435 
436  send_key_event(handler, GDK_KEY_Return);
437 }
438 
439 TEST(FlTextInputHandlerTest, MoveCursor) {
440  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
441  ::testing::NiceMock<flutter::testing::MockIMContext> context;
442  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
443 
444  g_autoptr(FlTextInputHandler) handler =
445  fl_text_input_handler_new(messenger, context, delegate);
446  EXPECT_NE(handler, nullptr);
447 
448  // set input config
449  g_autoptr(FlValue) config = build_input_config({.client_id = 1});
450  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
452  FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
453 
454  g_autoptr(FlValue) null = fl_value_new_null();
455  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
456  ::testing::Eq<FlBinaryMessenger*>(messenger),
457  ::testing::_, SuccessResponse(null), ::testing::_))
458  .WillOnce(::testing::Return(true));
459 
460  messenger.ReceiveMessage("flutter/textinput", set_client);
461 
462  // set editing state
463  g_autoptr(FlValue) state = build_editing_state({
464  .text = "Flutter",
465  .selection_base = 4,
466  .selection_extent = 4,
467  });
468  g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
469  FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
470 
471  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
472  ::testing::Eq<FlBinaryMessenger*>(messenger),
473  ::testing::_, SuccessResponse(null), ::testing::_))
474  .WillOnce(::testing::Return(true));
475 
476  messenger.ReceiveMessage("flutter/textinput", set_state);
477 
478  // move cursor to beginning
479  g_autoptr(FlValue) beginning = build_list({
480  fl_value_new_int(1), // client_id
482  .text = "Flutter",
483  .selection_base = 0,
484  .selection_extent = 0,
485  }),
486  });
487 
488  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
489  ::testing::Eq<FlBinaryMessenger*>(messenger),
490  ::testing::StrEq("flutter/textinput"),
491  MethodCall("TextInputClient.updateEditingState",
492  FlValueEq(beginning)),
493  ::testing::_, ::testing::_, ::testing::_));
494 
495  send_key_event(handler, GDK_KEY_Home);
496 
497  // move cursor to end
498  g_autoptr(FlValue) end = build_list({
499  fl_value_new_int(1), // client_id
501  .text = "Flutter",
502  .selection_base = 7,
503  .selection_extent = 7,
504  }),
505  });
506 
507  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
508  ::testing::Eq<FlBinaryMessenger*>(messenger),
509  ::testing::StrEq("flutter/textinput"),
510  MethodCall("TextInputClient.updateEditingState",
511  FlValueEq(end)),
512  ::testing::_, ::testing::_, ::testing::_));
513 
514  send_key_event(handler, GDK_KEY_End);
515 }
516 
517 TEST(FlTextInputHandlerTest, Select) {
518  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
519  ::testing::NiceMock<flutter::testing::MockIMContext> context;
520  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
521 
522  g_autoptr(FlTextInputHandler) handler =
523  fl_text_input_handler_new(messenger, context, delegate);
524  EXPECT_NE(handler, nullptr);
525 
526  // set input config
527  g_autoptr(FlValue) config = build_input_config({.client_id = 1});
528  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
530  FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
531 
532  g_autoptr(FlValue) null = fl_value_new_null();
533  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
534  ::testing::Eq<FlBinaryMessenger*>(messenger),
535  ::testing::_, SuccessResponse(null), ::testing::_))
536  .WillOnce(::testing::Return(true));
537 
538  messenger.ReceiveMessage("flutter/textinput", set_client);
539 
540  // set editing state
541  g_autoptr(FlValue) state = build_editing_state({
542  .text = "Flutter",
543  .selection_base = 4,
544  .selection_extent = 4,
545  });
546  g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
547  FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
548 
549  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
550  ::testing::Eq<FlBinaryMessenger*>(messenger),
551  ::testing::_, SuccessResponse(null), ::testing::_))
552  .WillOnce(::testing::Return(true));
553 
554  messenger.ReceiveMessage("flutter/textinput", set_state);
555 
556  // select to end
557  g_autoptr(FlValue) select_to_end = build_list({
558  fl_value_new_int(1), // client_id
560  .text = "Flutter",
561  .selection_base = 4,
562  .selection_extent = 7,
563  }),
564  });
565 
566  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
567  ::testing::Eq<FlBinaryMessenger*>(messenger),
568  ::testing::StrEq("flutter/textinput"),
569  MethodCall("TextInputClient.updateEditingState",
570  FlValueEq(select_to_end)),
571  ::testing::_, ::testing::_, ::testing::_));
572 
573  send_key_event(handler, GDK_KEY_End, GDK_SHIFT_MASK);
574 
575  // select to beginning
576  g_autoptr(FlValue) select_to_beginning = build_list({
577  fl_value_new_int(1), // client_id
579  .text = "Flutter",
580  .selection_base = 4,
581  .selection_extent = 0,
582  }),
583  });
584 
585  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
586  ::testing::Eq<FlBinaryMessenger*>(messenger),
587  ::testing::StrEq("flutter/textinput"),
588  MethodCall("TextInputClient.updateEditingState",
589  FlValueEq(select_to_beginning)),
590  ::testing::_, ::testing::_, ::testing::_));
591 
592  send_key_event(handler, GDK_KEY_Home, GDK_SHIFT_MASK);
593 }
594 
595 TEST(FlTextInputHandlerTest, Composing) {
596  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
597  ::testing::NiceMock<flutter::testing::MockIMContext> context;
598  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
599 
600  g_autoptr(FlTextInputHandler) handler =
601  fl_text_input_handler_new(messenger, context, delegate);
602  EXPECT_NE(handler, nullptr);
603 
604  g_signal_emit_by_name(context, "preedit-start", nullptr);
605 
606  // update
607  EXPECT_CALL(context,
608  gtk_im_context_get_preedit_string(
609  ::testing::Eq<GtkIMContext*>(context),
610  ::testing::A<gchar**>(), ::testing::_, ::testing::A<gint*>()))
611  .WillOnce(
612  ::testing::DoAll(::testing::SetArgPointee<1>(g_strdup("Flutter")),
613  ::testing::SetArgPointee<3>(0)));
614 
615  g_autoptr(FlValue) state = build_list({
616  fl_value_new_int(-1), // client_id
618  .text = "Flutter",
619  .selection_base = 0,
620  .selection_extent = 0,
621  .composing_base = 0,
622  .composing_extent = 7,
623  }),
624  });
625 
626  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
627  ::testing::Eq<FlBinaryMessenger*>(messenger),
628  ::testing::StrEq("flutter/textinput"),
629  MethodCall("TextInputClient.updateEditingState",
630  FlValueEq(state)),
631  ::testing::_, ::testing::_, ::testing::_));
632 
633  g_signal_emit_by_name(context, "preedit-changed", nullptr);
634 
635  // commit
636  g_autoptr(FlValue) commit = build_list({
637  fl_value_new_int(-1), // client_id
639  .text = "engine",
640  .selection_base = 6,
641  .selection_extent = 6,
642  }),
643  });
644 
645  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
646  ::testing::Eq<FlBinaryMessenger*>(messenger),
647  ::testing::StrEq("flutter/textinput"),
648  MethodCall("TextInputClient.updateEditingState",
649  FlValueEq(commit)),
650  ::testing::_, ::testing::_, ::testing::_));
651 
652  g_signal_emit_by_name(context, "commit", "engine", nullptr);
653 
654  // end
655  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
656  ::testing::Eq<FlBinaryMessenger*>(messenger),
657  ::testing::StrEq("flutter/textinput"),
658  MethodCall("TextInputClient.updateEditingState",
659  ::testing::_),
660  ::testing::_, ::testing::_, ::testing::_));
661 
662  g_signal_emit_by_name(context, "preedit-end", nullptr);
663 }
664 
665 TEST(FlTextInputHandlerTest, SurroundingText) {
666  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
667  ::testing::NiceMock<flutter::testing::MockIMContext> context;
668  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
669 
670  g_autoptr(FlTextInputHandler) handler =
671  fl_text_input_handler_new(messenger, context, delegate);
672  EXPECT_NE(handler, nullptr);
673 
674  // set input config
675  g_autoptr(FlValue) config = build_input_config({.client_id = 1});
676  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
678  FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
679 
680  g_autoptr(FlValue) null = fl_value_new_null();
681  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
682  ::testing::Eq<FlBinaryMessenger*>(messenger),
683  ::testing::_, SuccessResponse(null), ::testing::_))
684  .WillOnce(::testing::Return(true));
685 
686  messenger.ReceiveMessage("flutter/textinput", set_client);
687 
688  // set editing state
689  g_autoptr(FlValue) state = build_editing_state({
690  .text = "Flutter",
691  .selection_base = 3,
692  .selection_extent = 3,
693  });
694  g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
695  FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
696 
697  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
698  ::testing::Eq<FlBinaryMessenger*>(messenger),
699  ::testing::_, SuccessResponse(null), ::testing::_))
700  .WillOnce(::testing::Return(true));
701 
702  messenger.ReceiveMessage("flutter/textinput", set_state);
703 
704  // retrieve
705  EXPECT_CALL(context, gtk_im_context_set_surrounding(
706  ::testing::Eq<GtkIMContext*>(context),
707  ::testing::StrEq("Flutter"), 7, 3));
708 
709  gboolean retrieved = false;
710  g_signal_emit_by_name(context, "retrieve-surrounding", &retrieved, nullptr);
711  EXPECT_TRUE(retrieved);
712 
713  // delete
714  g_autoptr(FlValue) update = build_list({
715  fl_value_new_int(1), // client_id
717  .text = "Flutr",
718  .selection_base = 3,
719  .selection_extent = 3,
720  }),
721  });
722 
723  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
724  ::testing::Eq<FlBinaryMessenger*>(messenger),
725  ::testing::StrEq("flutter/textinput"),
726  MethodCall("TextInputClient.updateEditingState",
727  FlValueEq(update)),
728  ::testing::_, ::testing::_, ::testing::_));
729 
730  gboolean deleted = false;
731  g_signal_emit_by_name(context, "delete-surrounding", 1, 2, &deleted, nullptr);
732  EXPECT_TRUE(deleted);
733 }
734 
735 TEST(FlTextInputHandlerTest, SetMarkedTextRect) {
736  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
737  ::testing::NiceMock<flutter::testing::MockIMContext> context;
738  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
739 
740  g_autoptr(FlTextInputHandler) handler =
741  fl_text_input_handler_new(messenger, context, delegate);
742  EXPECT_NE(handler, nullptr);
743 
744  g_signal_emit_by_name(context, "preedit-start", nullptr);
745 
746  // set editable size and transform
747  g_autoptr(FlValue) size_and_transform = build_map({
748  {
749  "transform",
750  build_list({
760  fl_value_new_float(10),
761  fl_value_new_float(11),
762  fl_value_new_float(12),
763  fl_value_new_float(13),
764  fl_value_new_float(14),
765  fl_value_new_float(15),
766  fl_value_new_float(16),
767  }),
768  },
769  });
770  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
771  g_autoptr(GBytes) set_editable_size_and_transform =
773  FL_METHOD_CODEC(codec), "TextInput.setEditableSizeAndTransform",
774  size_and_transform, nullptr);
775 
776  g_autoptr(FlValue) null = fl_value_new_null();
777  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
778  ::testing::Eq<FlBinaryMessenger*>(messenger),
779  ::testing::_, SuccessResponse(null), ::testing::_))
780  .WillOnce(::testing::Return(true));
781 
782  messenger.ReceiveMessage("flutter/textinput",
784 
785  // set marked text rect
786  g_autoptr(FlValue) rect = build_map({
787  {"x", fl_value_new_float(1)},
788  {"y", fl_value_new_float(2)},
789  {"width", fl_value_new_float(3)},
790  {"height", fl_value_new_float(4)},
791  });
793  FL_METHOD_CODEC(codec), "TextInput.setMarkedTextRect", rect, nullptr);
794 
795  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
796  ::testing::Eq<FlBinaryMessenger*>(messenger),
797  ::testing::_, SuccessResponse(null), ::testing::_))
798  .WillOnce(::testing::Return(true));
799 
801  ::testing::Eq<FlTextInputViewDelegate*>(delegate),
802  ::testing::Eq(27), ::testing::Eq(32), ::testing::_,
803  ::testing::_))
804  .WillOnce(::testing::DoAll(::testing::SetArgPointee<3>(123),
805  ::testing::SetArgPointee<4>(456)));
806 
807  EXPECT_CALL(context, gtk_im_context_set_cursor_location(
808  ::testing::Eq<GtkIMContext*>(context),
809  ::testing::Pointee(::testing::AllOf(
810  ::testing::Field(&GdkRectangle::x, 123),
811  ::testing::Field(&GdkRectangle::y, 456),
812  ::testing::Field(&GdkRectangle::width, 0),
813  ::testing::Field(&GdkRectangle::height, 0)))));
814 
815  messenger.ReceiveMessage("flutter/textinput", set_marked_text_rect);
816 }
817 
818 TEST(FlTextInputHandlerTest, TextInputTypeNone) {
819  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
820  ::testing::NiceMock<flutter::testing::MockIMContext> context;
821  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
822 
823  g_autoptr(FlTextInputHandler) handler =
824  fl_text_input_handler_new(messenger, context, delegate);
825  EXPECT_NE(handler, nullptr);
826 
827  g_autoptr(FlValue) args = build_input_config({
828  .client_id = 1,
829  .input_type = "TextInputType.none",
830  });
831  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
833  FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr);
834 
835  g_autoptr(FlValue) null = fl_value_new_null();
836  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
837  ::testing::Eq<FlBinaryMessenger*>(messenger),
838  ::testing::A<FlBinaryMessengerResponseHandle*>(),
839  SuccessResponse(null), ::testing::A<GError**>()))
840  .WillOnce(::testing::Return(true));
841 
842  messenger.ReceiveMessage("flutter/textinput", set_client);
843 
844  EXPECT_CALL(context,
845  gtk_im_context_focus_in(::testing::Eq<GtkIMContext*>(context)))
846  .Times(0);
847  EXPECT_CALL(context,
848  gtk_im_context_focus_out(::testing::Eq<GtkIMContext*>(context)));
849 
850  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
851  ::testing::Eq<FlBinaryMessenger*>(messenger),
852  ::testing::_, SuccessResponse(null), ::testing::_))
853  .WillOnce(::testing::Return(true));
854 
855  g_autoptr(GBytes) show = fl_method_codec_encode_method_call(
856  FL_METHOD_CODEC(codec), "TextInput.show", nullptr, nullptr);
857 
858  messenger.ReceiveMessage("flutter/textinput", show);
859 }
860 
861 TEST(FlTextInputHandlerTest, TextEditingDelta) {
862  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
863  ::testing::NiceMock<flutter::testing::MockIMContext> context;
864  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
865 
866  g_autoptr(FlTextInputHandler) handler =
867  fl_text_input_handler_new(messenger, context, delegate);
868  EXPECT_NE(handler, nullptr);
869 
870  // set config
871  g_autoptr(FlValue) args = build_input_config({
872  .client_id = 1,
873  .enable_delta_model = true,
874  });
875  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
877  FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr);
878 
879  g_autoptr(FlValue) null = fl_value_new_null();
880  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
881  ::testing::Eq<FlBinaryMessenger*>(messenger),
882  ::testing::A<FlBinaryMessengerResponseHandle*>(),
883  SuccessResponse(null), ::testing::A<GError**>()))
884  .WillOnce(::testing::Return(true));
885 
886  messenger.ReceiveMessage("flutter/textinput", set_client);
887 
888  // set editing state
889  g_autoptr(FlValue) state = build_editing_state({
890  .text = "Flutter",
891  .selection_base = 7,
892  .selection_extent = 7,
893  });
894  g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
895  FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
896 
897  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
898  ::testing::Eq<FlBinaryMessenger*>(messenger),
899  ::testing::_, SuccessResponse(null), ::testing::_))
900  .WillOnce(::testing::Return(true));
901 
902  messenger.ReceiveMessage("flutter/textinput", set_state);
903 
904  // update editing state with deltas
905  g_autoptr(FlValue) deltas = build_list({
906  fl_value_new_int(1), // client_id
907  build_map({{
908  "deltas",
909  build_list({
911  .old_text = "Flutter",
912  .delta_text = "Flutter",
913  .delta_start = 7,
914  .delta_end = 7,
915  .selection_base = 0,
916  .selection_extent = 0,
917  }),
918  }),
919  }}),
920  });
921 
922  EXPECT_CALL(messenger,
924  ::testing::Eq<FlBinaryMessenger*>(messenger),
925  ::testing::StrEq("flutter/textinput"),
926  MethodCall("TextInputClient.updateEditingStateWithDeltas",
927  FlValueEq(deltas)),
928  ::testing::_, ::testing::_, ::testing::_));
929 
930  send_key_event(handler, GDK_KEY_Home);
931 }
932 
933 TEST(FlTextInputHandlerTest, ComposingDelta) {
934  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
935  ::testing::NiceMock<flutter::testing::MockIMContext> context;
936  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
937 
938  g_autoptr(FlTextInputHandler) handler =
939  fl_text_input_handler_new(messenger, context, delegate);
940  EXPECT_NE(handler, nullptr);
941 
942  // set config
943  g_autoptr(FlValue) args = build_input_config({
944  .client_id = 1,
945  .enable_delta_model = true,
946  });
947  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
949  FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr);
950 
951  g_autoptr(FlValue) null = fl_value_new_null();
952  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
953  ::testing::Eq<FlBinaryMessenger*>(messenger),
954  ::testing::A<FlBinaryMessengerResponseHandle*>(),
955  SuccessResponse(null), ::testing::A<GError**>()))
956  .WillOnce(::testing::Return(true));
957 
958  messenger.ReceiveMessage("flutter/textinput", set_client);
959 
960  g_signal_emit_by_name(context, "preedit-start", nullptr);
961 
962  // update
963  EXPECT_CALL(context,
964  gtk_im_context_get_preedit_string(
965  ::testing::Eq<GtkIMContext*>(context),
966  ::testing::A<gchar**>(), ::testing::_, ::testing::A<gint*>()))
967  .WillOnce(
968  ::testing::DoAll(::testing::SetArgPointee<1>(g_strdup("Flutter ")),
969  ::testing::SetArgPointee<3>(8)));
970 
971  g_autoptr(FlValue) update = build_list({
972  fl_value_new_int(1), // client_id
973  build_map({{
974  "deltas",
975  build_list({
977  .old_text = "",
978  .delta_text = "Flutter ",
979  .delta_start = 0,
980  .delta_end = 0,
981  .selection_base = 8,
982  .selection_extent = 8,
983  .composing_base = 0,
984  .composing_extent = 8,
985  }),
986  }),
987  }}),
988  });
989 
990  EXPECT_CALL(messenger,
992  ::testing::Eq<FlBinaryMessenger*>(messenger),
993  ::testing::StrEq("flutter/textinput"),
994  MethodCall("TextInputClient.updateEditingStateWithDeltas",
995  FlValueEq(update)),
996  ::testing::_, ::testing::_, ::testing::_));
997 
998  g_signal_emit_by_name(context, "preedit-changed", nullptr);
999 
1000  // commit
1001  g_autoptr(FlValue) commit = build_list({
1002  fl_value_new_int(1), // client_id
1003  build_map({{
1004  "deltas",
1005  build_list({
1007  .old_text = "Flutter ",
1008  .delta_text = "Flutter engine",
1009  .delta_start = 0,
1010  .delta_end = 8,
1011  .selection_base = 14,
1012  .selection_extent = 14,
1013  .composing_base = -1,
1014  .composing_extent = -1,
1015  }),
1016  }),
1017  }}),
1018  });
1019 
1020  EXPECT_CALL(messenger,
1022  ::testing::Eq<FlBinaryMessenger*>(messenger),
1023  ::testing::StrEq("flutter/textinput"),
1024  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1025  FlValueEq(commit)),
1026  ::testing::_, ::testing::_, ::testing::_));
1027 
1028  g_signal_emit_by_name(context, "commit", "Flutter engine", nullptr);
1029 
1030  // end
1031  g_autoptr(FlValue) end = build_list({
1032  fl_value_new_int(1), // client_id
1033  build_map({{
1034  "deltas",
1035  build_list({
1037  .old_text = "Flutter engine",
1038  .selection_base = 14,
1039  .selection_extent = 14,
1040  }),
1041  }),
1042  }}),
1043  });
1044 
1045  EXPECT_CALL(messenger,
1047  ::testing::Eq<FlBinaryMessenger*>(messenger),
1048  ::testing::StrEq("flutter/textinput"),
1049  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1050  FlValueEq(end)),
1051  ::testing::_, ::testing::_, ::testing::_));
1052 
1053  g_signal_emit_by_name(context, "preedit-end", nullptr);
1054 }
1055 
1056 TEST(FlTextInputHandlerTest, NonComposingDelta) {
1057  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
1058  ::testing::NiceMock<flutter::testing::MockIMContext> context;
1059  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
1060 
1061  g_autoptr(FlTextInputHandler) handler =
1062  fl_text_input_handler_new(messenger, context, delegate);
1063  EXPECT_NE(handler, nullptr);
1064 
1065  // set config
1066  g_autoptr(FlValue) args = build_input_config({
1067  .client_id = 1,
1068  .enable_delta_model = true,
1069  });
1070  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
1071  g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
1072  FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr);
1073 
1074  g_autoptr(FlValue) null = fl_value_new_null();
1075  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
1076  ::testing::Eq<FlBinaryMessenger*>(messenger),
1077  ::testing::A<FlBinaryMessengerResponseHandle*>(),
1078  SuccessResponse(null), ::testing::A<GError**>()))
1079  .WillOnce(::testing::Return(true));
1080 
1081  messenger.ReceiveMessage("flutter/textinput", set_client);
1082 
1083  // commit F
1084  g_autoptr(FlValue) commit = build_list({
1085  fl_value_new_int(1), // client_id
1086  build_map({{
1087  "deltas",
1088  build_list({
1090  .old_text = "",
1091  .delta_text = "F",
1092  .delta_start = 0,
1093  .delta_end = 0,
1094  .selection_base = 1,
1095  .selection_extent = 1,
1096  .composing_base = -1,
1097  .composing_extent = -1,
1098  }),
1099  }),
1100  }}),
1101  });
1102 
1103  EXPECT_CALL(messenger,
1105  ::testing::Eq<FlBinaryMessenger*>(messenger),
1106  ::testing::StrEq("flutter/textinput"),
1107  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1108  FlValueEq(commit)),
1109  ::testing::_, ::testing::_, ::testing::_));
1110 
1111  g_signal_emit_by_name(context, "commit", "F", nullptr);
1112 
1113  // commit l
1114  g_autoptr(FlValue) commitL = build_list({
1115  fl_value_new_int(1), // client_id
1116  build_map({{
1117  "deltas",
1118  build_list({
1120  .old_text = "F",
1121  .delta_text = "l",
1122  .delta_start = 1,
1123  .delta_end = 1,
1124  .selection_base = 2,
1125  .selection_extent = 2,
1126  .composing_base = -1,
1127  .composing_extent = -1,
1128  }),
1129  }),
1130  }}),
1131  });
1132 
1133  EXPECT_CALL(messenger,
1135  ::testing::Eq<FlBinaryMessenger*>(messenger),
1136  ::testing::StrEq("flutter/textinput"),
1137  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1138  FlValueEq(commitL)),
1139  ::testing::_, ::testing::_, ::testing::_));
1140 
1141  g_signal_emit_by_name(context, "commit", "l", nullptr);
1142 
1143  // commit u
1144  g_autoptr(FlValue) commitU = build_list({
1145  fl_value_new_int(1), // client_id
1146  build_map({{
1147  "deltas",
1148  build_list({
1150  .old_text = "Fl",
1151  .delta_text = "u",
1152  .delta_start = 2,
1153  .delta_end = 2,
1154  .selection_base = 3,
1155  .selection_extent = 3,
1156  .composing_base = -1,
1157  .composing_extent = -1,
1158  }),
1159  }),
1160  }}),
1161  });
1162 
1163  EXPECT_CALL(messenger,
1165  ::testing::Eq<FlBinaryMessenger*>(messenger),
1166  ::testing::StrEq("flutter/textinput"),
1167  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1168  FlValueEq(commitU)),
1169  ::testing::_, ::testing::_, ::testing::_));
1170 
1171  g_signal_emit_by_name(context, "commit", "u", nullptr);
1172 
1173  // commit t
1174  g_autoptr(FlValue) commitTa = build_list({
1175  fl_value_new_int(1), // client_id
1176  build_map({{
1177  "deltas",
1178  build_list({
1180  .old_text = "Flu",
1181  .delta_text = "t",
1182  .delta_start = 3,
1183  .delta_end = 3,
1184  .selection_base = 4,
1185  .selection_extent = 4,
1186  .composing_base = -1,
1187  .composing_extent = -1,
1188  }),
1189  }),
1190  }}),
1191  });
1192 
1193  EXPECT_CALL(messenger,
1195  ::testing::Eq<FlBinaryMessenger*>(messenger),
1196  ::testing::StrEq("flutter/textinput"),
1197  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1198  FlValueEq(commitTa)),
1199  ::testing::_, ::testing::_, ::testing::_));
1200 
1201  g_signal_emit_by_name(context, "commit", "t", nullptr);
1202 
1203  // commit t again
1204  g_autoptr(FlValue) commitTb = build_list({
1205  fl_value_new_int(1), // client_id
1206  build_map({{
1207  "deltas",
1208  build_list({
1210  .old_text = "Flut",
1211  .delta_text = "t",
1212  .delta_start = 4,
1213  .delta_end = 4,
1214  .selection_base = 5,
1215  .selection_extent = 5,
1216  .composing_base = -1,
1217  .composing_extent = -1,
1218  }),
1219  }),
1220  }}),
1221  });
1222 
1223  EXPECT_CALL(messenger,
1225  ::testing::Eq<FlBinaryMessenger*>(messenger),
1226  ::testing::StrEq("flutter/textinput"),
1227  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1228  FlValueEq(commitTb)),
1229  ::testing::_, ::testing::_, ::testing::_));
1230 
1231  g_signal_emit_by_name(context, "commit", "t", nullptr);
1232 
1233  // commit e
1234  g_autoptr(FlValue) commitE = build_list({
1235  fl_value_new_int(1), // client_id
1236  build_map({{
1237  "deltas",
1238  build_list({
1240  .old_text = "Flutt",
1241  .delta_text = "e",
1242  .delta_start = 5,
1243  .delta_end = 5,
1244  .selection_base = 6,
1245  .selection_extent = 6,
1246  .composing_base = -1,
1247  .composing_extent = -1,
1248  }),
1249  }),
1250  }}),
1251  });
1252 
1253  EXPECT_CALL(messenger,
1255  ::testing::Eq<FlBinaryMessenger*>(messenger),
1256  ::testing::StrEq("flutter/textinput"),
1257  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1258  FlValueEq(commitE)),
1259  ::testing::_, ::testing::_, ::testing::_));
1260 
1261  g_signal_emit_by_name(context, "commit", "e", nullptr);
1262 
1263  // commit r
1264  g_autoptr(FlValue) commitR = build_list({
1265  fl_value_new_int(1), // client_id
1266  build_map({{
1267  "deltas",
1268  build_list({
1270  .old_text = "Flutte",
1271  .delta_text = "r",
1272  .delta_start = 6,
1273  .delta_end = 6,
1274  .selection_base = 7,
1275  .selection_extent = 7,
1276  .composing_base = -1,
1277  .composing_extent = -1,
1278  }),
1279  }),
1280  }}),
1281  });
1282 
1283  EXPECT_CALL(messenger,
1285  ::testing::Eq<FlBinaryMessenger*>(messenger),
1286  ::testing::StrEq("flutter/textinput"),
1287  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1288  FlValueEq(commitR)),
1289  ::testing::_, ::testing::_, ::testing::_));
1290 
1291  g_signal_emit_by_name(context, "commit", "r", nullptr);
1292 }
EditingDelta::delta_end
int delta_end
Definition: fl_text_input_handler_test.cc:159
build_list
static FlValue * build_list(std::vector< FlValue * > args)
Definition: fl_text_input_handler_test.cc:107
fl_json_method_codec_new
G_MODULE_EXPORT FlJsonMethodCodec * fl_json_method_codec_new()
Definition: fl_json_method_codec.cc:205
EditingDelta
Definition: fl_text_input_handler_test.cc:155
fl_method_codec_encode_method_call
GBytes * fl_method_codec_encode_method_call(FlMethodCodec *self, const gchar *name, FlValue *args, GError **error)
Definition: fl_method_codec.cc:16
MethodCallMatcher::MethodCallMatcher
MethodCallMatcher(::testing::Matcher< std::string > name, ::testing::Matcher< FlValue * > args)
Definition: fl_text_input_handler_test.cc:47
fl_key_event_new_from_gdk_event
FlKeyEvent * fl_key_event_new_from_gdk_event(GdkEvent *event)
Definition: fl_key_event.cc:53
InputConfig::enable_delta_model
gboolean enable_delta_model
Definition: fl_text_input_handler_test.cc:119
fl_text_input_view_delegate_translate_coordinates
void fl_text_input_view_delegate_translate_coordinates(FlTextInputViewDelegate *self, gint view_x, gint view_y, gint *window_x, gint *window_y)
Definition: fl_text_input_view_delegate.cc:14
MethodCallMatcher::DescribeNegationTo
void DescribeNegationTo(std::ostream *os) const
Definition: fl_text_input_handler_test.cc:82
fl_value_set_string_take
G_MODULE_EXPORT void fl_value_set_string_take(FlValue *self, const gchar *key, FlValue *value)
Definition: fl_value.cc:650
set_client
static FlMethodResponse * set_client(FlTextInputHandler *self, FlValue *args)
Definition: fl_text_input_handler.cc:364
fl_value_new_list
G_MODULE_EXPORT FlValue * fl_value_new_list()
Definition: fl_value.cc:349
EditingDelta::composing_extent
int composing_extent
Definition: fl_text_input_handler_test.cc:163
TEST
TEST(FlTextInputHandlerTest, MessageHandler)
Definition: fl_text_input_handler_test.cc:191
show
static FlMethodResponse * show(FlTextInputHandler *self)
Definition: fl_text_input_handler.cc:417
EditingState::composing_base
int composing_base
Definition: fl_text_input_handler_test.cc:139
fl_value_new_bool
G_MODULE_EXPORT FlValue * fl_value_new_bool(bool value)
Definition: fl_value.cc:255
FlValue
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition: fl_value.h:42
fl_text_input_handler.h
fl_value_new_null
G_MODULE_EXPORT FlValue * fl_value_new_null()
Definition: fl_value.cc:251
fl_text_input_handler_new
FlTextInputHandler * fl_text_input_handler_new(FlBinaryMessenger *messenger, GtkIMContext *im_context, FlTextInputViewDelegate *view_delegate)
Definition: fl_text_input_handler.cc:747
InputConfig::client_id
int64_t client_id
Definition: fl_text_input_handler_test.cc:116
fl_method_response_get_result
G_MODULE_EXPORT FlValue * fl_method_response_get_result(FlMethodResponse *self, GError **error)
Definition: fl_method_response.cc:82
EditingDelta::delta_text
const gchar * delta_text
Definition: fl_text_input_handler_test.cc:157
EditingState::selection_base
int selection_base
Definition: fl_text_input_handler_test.cc:137
EditingDelta::delta_start
int delta_start
Definition: fl_text_input_handler_test.cc:158
fl_value_new_int
G_MODULE_EXPORT FlValue * fl_value_new_int(int64_t value)
Definition: fl_value.cc:262
EditingDelta::selection_extent
int selection_extent
Definition: fl_text_input_handler_test.cc:161
state
AtkStateType state
Definition: fl_accessible_node.cc:10
set_editable_size_and_transform
static FlMethodResponse * set_editable_size_and_transform(FlTextInputHandler *self, FlValue *args)
Definition: fl_text_input_handler.cc:518
build_map
static FlValue * build_map(std::map< const gchar *, FlValue * > args)
Definition: fl_text_input_handler_test.cc:99
printTo
void printTo(FlMethodResponse *response, ::std::ostream *os)
Definition: fl_text_input_handler_test.cc:22
fl_value_new_map
G_MODULE_EXPORT FlValue * fl_value_new_map()
Definition: fl_value.cc:366
EditingState::text
const gchar * text
Definition: fl_text_input_handler_test.cc:136
MethodCallMatcher::DescribeTo
void DescribeTo(std::ostream *os) const
Definition: fl_text_input_handler_test.cc:75
method_call
G_BEGIN_DECLS G_MODULE_EXPORT FlMethodCall * method_call
Definition: fl_method_channel.h:120
fl_binary_messenger.h
fl_text_input_handler_filter_keypress
gboolean fl_text_input_handler_filter_keypress(FlTextInputHandler *self, FlKeyEvent *event)
Definition: fl_text_input_handler.cc:778
fl_method_codec_decode_method_call
gboolean fl_method_codec_decode_method_call(FlMethodCodec *self, GBytes *message, gchar **name, FlValue **args, GError **error)
Definition: fl_method_codec.cc:27
EditingDelta::old_text
const gchar * old_text
Definition: fl_text_input_handler_test.cc:156
MethodCallMatcher::is_gtest_matcher
void is_gtest_matcher
Definition: fl_platform_handler_test.cc:33
EditingState
Definition: fl_text_input_handler_test.cc:135
EditingState::composing_extent
int composing_extent
Definition: fl_text_input_handler_test.cc:140
end
glong glong end
Definition: fl_accessible_text_field.cc:40
set_marked_text_rect
static FlMethodResponse * set_marked_text_rect(FlTextInputHandler *self, FlValue *args)
Definition: fl_text_input_handler.cc:542
fl_value_append_take
G_MODULE_EXPORT void fl_value_append_take(FlValue *self, FlValue *value)
Definition: fl_value.cc:600
fl_method_codec_decode_response
FlMethodResponse * fl_method_codec_decode_response(FlMethodCodec *self, GBytes *message, GError **error)
Definition: fl_method_codec.cc:62
MethodCallMatcher::MatchAndExplain
bool MatchAndExplain(GBytes *method_call, ::testing::MatchResultListener *result_listener) const
Definition: fl_text_input_handler_test.cc:51
fl_value_equal
G_MODULE_EXPORT bool fl_value_equal(FlValue *a, FlValue *b)
Definition: fl_value.cc:471
InputConfig
Definition: fl_text_input_handler_test.cc:115
EditingDelta::composing_base
int composing_base
Definition: fl_text_input_handler_test.cc:162
MethodCall
::testing::Matcher< GBytes * > MethodCall(const std::string &name, ::testing::Matcher< FlValue * > args)
Definition: fl_text_input_handler_test.cc:94
height
const uint8_t uint32_t uint32_t * height
Definition: fl_pixel_buffer_texture_test.cc:39
result
GAsyncResult * result
Definition: fl_text_input_handler.cc:106
MethodCallMatcher
Definition: fl_platform_handler_test.cc:31
send_key_event
static void send_key_event(FlTextInputHandler *handler, gint keyval, gint state=0)
Definition: fl_text_input_handler_test.cc:181
build_input_config
static FlValue * build_input_config(InputConfig config)
Definition: fl_text_input_handler_test.cc:122
fl_value.h
args
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
Definition: fl_event_channel.h:89
error
const uint8_t uint32_t uint32_t GError ** error
Definition: fl_pixel_buffer_texture_test.cc:40
build_editing_state
static FlValue * build_editing_state(EditingState state)
Definition: fl_text_input_handler_test.cc:143
MATCHER_P
MATCHER_P(SuccessResponse, result, "")
Definition: fl_text_input_handler_test.cc:27
InputConfig::input_type
const gchar * input_type
Definition: fl_text_input_handler_test.cc:117
EditingState::selection_extent
int selection_extent
Definition: fl_text_input_handler_test.cc:138
InputConfig::input_action
const gchar * input_action
Definition: fl_text_input_handler_test.cc:118
fl_value_new_float
G_MODULE_EXPORT FlValue * fl_value_new_float(double value)
Definition: fl_value.cc:269
fl_binary_messenger_send_response
G_MODULE_EXPORT gboolean fl_binary_messenger_send_response(FlBinaryMessenger *self, FlBinaryMessengerResponseHandle *response_handle, GBytes *response, GError **error)
Definition: fl_binary_messenger.cc:430
width
const uint8_t uint32_t * width
Definition: fl_pixel_buffer_texture_test.cc:38
flutter::MessageHandler
std::function< void(const T &message, const MessageReply< T > &reply)> MessageHandler
Definition: basic_message_channel.h:51
fl_method_codec_private.h
build_editing_delta
static FlValue * build_editing_delta(EditingDelta delta)
Definition: fl_text_input_handler_test.cc:166
value
uint8_t value
Definition: fl_standard_message_codec.cc:36
fl_binary_messenger_send_on_channel
G_MODULE_EXPORT void fl_binary_messenger_send_on_channel(FlBinaryMessenger *self, const gchar *channel, GBytes *message, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Definition: fl_binary_messenger.cc:443
fl_json_method_codec.h
fl_value_new_string
G_MODULE_EXPORT FlValue * fl_value_new_string(const gchar *value)
Definition: fl_value.cc:276
EditingDelta::selection_base
int selection_base
Definition: fl_text_input_handler_test.cc:160