Flutter Linux Embedder
fl_text_input_plugin_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(FlTextInputPlugin* plugin,
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  FlKeyEvent* key_event = fl_key_event_new_from_gdk_event(gdk_event);
188  fl_text_input_plugin_filter_keypress(plugin, key_event);
189  fl_key_event_dispose(key_event);
190 }
191 
192 TEST(FlTextInputPluginTest, MessageHandler) {
193  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
194  ::testing::NiceMock<flutter::testing::MockIMContext> context;
195  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
196 
197  g_autoptr(FlTextInputPlugin) plugin =
198  fl_text_input_plugin_new(messenger, context, delegate);
199  EXPECT_NE(plugin, nullptr);
200 
201  EXPECT_TRUE(messenger.HasMessageHandler("flutter/textinput"));
202 }
203 
204 TEST(FlTextInputPluginTest, SetClient) {
205  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
206  ::testing::NiceMock<flutter::testing::MockIMContext> context;
207  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
208 
209  g_autoptr(FlTextInputPlugin) plugin =
210  fl_text_input_plugin_new(messenger, context, delegate);
211  EXPECT_NE(plugin, nullptr);
212 
213  g_autoptr(FlValue) args = build_input_config({.client_id = 1});
214  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
215  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
216  FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr);
217 
218  g_autoptr(FlValue) null = fl_value_new_null();
219  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
220  ::testing::Eq<FlBinaryMessenger*>(messenger),
221  ::testing::_, SuccessResponse(null), ::testing::_))
222  .WillOnce(::testing::Return(true));
223 
224  messenger.ReceiveMessage("flutter/textinput", message);
225 }
226 
227 TEST(FlTextInputPluginTest, Show) {
228  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
229  ::testing::NiceMock<flutter::testing::MockIMContext> context;
230  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
231 
232  g_autoptr(FlTextInputPlugin) plugin =
233  fl_text_input_plugin_new(messenger, context, delegate);
234  EXPECT_NE(plugin, nullptr);
235 
236  EXPECT_CALL(context,
237  gtk_im_context_focus_in(::testing::Eq<GtkIMContext*>(context)));
238 
239  g_autoptr(FlValue) null = fl_value_new_null();
240  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
241  ::testing::Eq<FlBinaryMessenger*>(messenger),
242  ::testing::_, SuccessResponse(null), ::testing::_))
243  .WillOnce(::testing::Return(true));
244 
245  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
246  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
247  FL_METHOD_CODEC(codec), "TextInput.show", nullptr, nullptr);
248 
249  messenger.ReceiveMessage("flutter/textinput", message);
250 }
251 
252 TEST(FlTextInputPluginTest, Hide) {
253  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
254  ::testing::NiceMock<flutter::testing::MockIMContext> context;
255  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
256 
257  g_autoptr(FlTextInputPlugin) plugin =
258  fl_text_input_plugin_new(messenger, context, delegate);
259  EXPECT_NE(plugin, nullptr);
260 
261  EXPECT_CALL(context,
262  gtk_im_context_focus_out(::testing::Eq<GtkIMContext*>(context)));
263 
264  g_autoptr(FlValue) null = fl_value_new_null();
265  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
266  ::testing::Eq<FlBinaryMessenger*>(messenger),
267  ::testing::_, SuccessResponse(null), ::testing::_))
268  .WillOnce(::testing::Return(true));
269 
270  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
271  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
272  FL_METHOD_CODEC(codec), "TextInput.hide", nullptr, nullptr);
273 
274  messenger.ReceiveMessage("flutter/textinput", message);
275 }
276 
277 TEST(FlTextInputPluginTest, ClearClient) {
278  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
279  ::testing::NiceMock<flutter::testing::MockIMContext> context;
280  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
281 
282  g_autoptr(FlTextInputPlugin) plugin =
283  fl_text_input_plugin_new(messenger, context, delegate);
284  EXPECT_NE(plugin, nullptr);
285 
286  g_autoptr(FlValue) null = fl_value_new_null();
287  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
288  ::testing::Eq<FlBinaryMessenger*>(messenger),
289  ::testing::_, SuccessResponse(null), ::testing::_))
290  .WillOnce(::testing::Return(true));
291 
292  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
293  g_autoptr(GBytes) message = fl_method_codec_encode_method_call(
294  FL_METHOD_CODEC(codec), "TextInput.clearClient", nullptr, nullptr);
295 
296  messenger.ReceiveMessage("flutter/textinput", message);
297 }
298 
299 TEST(FlTextInputPluginTest, PerformAction) {
300  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
301  ::testing::NiceMock<flutter::testing::MockIMContext> context;
302  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
303 
304  g_autoptr(FlTextInputPlugin) plugin =
305  fl_text_input_plugin_new(messenger, context, delegate);
306  EXPECT_NE(plugin, nullptr);
307 
308  // set input config
309  g_autoptr(FlValue) config = build_input_config({
310  .client_id = 1,
311  .input_type = "TextInputType.multiline",
312  .input_action = "TextInputAction.newline",
313  });
314  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
316  FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
317 
318  g_autoptr(FlValue) null = fl_value_new_null();
319  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
320  ::testing::Eq<FlBinaryMessenger*>(messenger),
321  ::testing::_, SuccessResponse(null), ::testing::_))
322  .WillOnce(::testing::Return(true));
323 
324  messenger.ReceiveMessage("flutter/textinput", set_client);
325 
326  // set editing state
327  g_autoptr(FlValue) state = build_editing_state({
328  .text = "Flutter",
329  .selection_base = 7,
330  .selection_extent = 7,
331  });
332  g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
333  FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
334 
335  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
336  ::testing::Eq<FlBinaryMessenger*>(messenger),
337  ::testing::_, SuccessResponse(null), ::testing::_))
338  .WillOnce(::testing::Return(true));
339 
340  messenger.ReceiveMessage("flutter/textinput", set_state);
341 
342  // update editing state
343  g_autoptr(FlValue) new_state = build_list({
344  fl_value_new_int(1), // client_id
346  .text = "Flutter\n",
347  .selection_base = 8,
348  .selection_extent = 8,
349  }),
350  });
351 
352  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
353  ::testing::Eq<FlBinaryMessenger*>(messenger),
354  ::testing::StrEq("flutter/textinput"),
355  MethodCall("TextInputClient.updateEditingState",
356  FlValueEq(new_state)),
357  ::testing::_, ::testing::_, ::testing::_));
358 
359  // perform action
360  g_autoptr(FlValue) action = build_list({
361  fl_value_new_int(1), // client_id
362  fl_value_new_string("TextInputAction.newline"),
363  });
364 
365  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
366  ::testing::Eq<FlBinaryMessenger*>(messenger),
367  ::testing::StrEq("flutter/textinput"),
368  MethodCall("TextInputClient.performAction",
369  FlValueEq(action)),
370  ::testing::_, ::testing::_, ::testing::_));
371 
372  send_key_event(plugin, GDK_KEY_Return);
373 }
374 
375 // Regression test for https://github.com/flutter/flutter/issues/125879.
376 TEST(FlTextInputPluginTest, MultilineWithSendAction) {
377  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
378  ::testing::NiceMock<flutter::testing::MockIMContext> context;
379  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
380 
381  g_autoptr(FlTextInputPlugin) plugin =
382  fl_text_input_plugin_new(messenger, context, delegate);
383  EXPECT_NE(plugin, nullptr);
384 
385  // Set input config.
386  g_autoptr(FlValue) config = build_input_config({
387  .client_id = 1,
388  .input_type = "TextInputType.multiline",
389  .input_action = "TextInputAction.send",
390  });
391  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
393  FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
394 
395  g_autoptr(FlValue) null = fl_value_new_null();
396  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
397  ::testing::Eq<FlBinaryMessenger*>(messenger),
398  ::testing::_, SuccessResponse(null), ::testing::_))
399  .WillOnce(::testing::Return(true));
400 
401  messenger.ReceiveMessage("flutter/textinput", set_client);
402 
403  // Set editing state.
404  g_autoptr(FlValue) state = build_editing_state({
405  .text = "Flutter",
406  .selection_base = 7,
407  .selection_extent = 7,
408  });
409  g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
410  FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
411 
412  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
413  ::testing::Eq<FlBinaryMessenger*>(messenger),
414  ::testing::_, SuccessResponse(null), ::testing::_))
415  .WillOnce(::testing::Return(true));
416 
417  messenger.ReceiveMessage("flutter/textinput", set_state);
418 
419  // Perform action.
420  g_autoptr(FlValue) action = build_list({
421  fl_value_new_int(1), // client_id
422  fl_value_new_string("TextInputAction.send"),
423  });
424 
425  // Because the input action is not set to TextInputAction.newline, the next
426  // expected call is "TextInputClient.performAction". If the input action was
427  // set to TextInputAction.newline the next call would be
428  // "TextInputClient.updateEditingState" (this case is tested in the test named
429  // 'PerformAction').
430  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
431  ::testing::Eq<FlBinaryMessenger*>(messenger),
432  ::testing::StrEq("flutter/textinput"),
433  MethodCall("TextInputClient.performAction",
434  FlValueEq(action)),
435  ::testing::_, ::testing::_, ::testing::_));
436 
437  send_key_event(plugin, GDK_KEY_Return);
438 }
439 
440 TEST(FlTextInputPluginTest, MoveCursor) {
441  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
442  ::testing::NiceMock<flutter::testing::MockIMContext> context;
443  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
444 
445  g_autoptr(FlTextInputPlugin) plugin =
446  fl_text_input_plugin_new(messenger, context, delegate);
447  EXPECT_NE(plugin, nullptr);
448 
449  // set input config
450  g_autoptr(FlValue) config = build_input_config({.client_id = 1});
451  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
453  FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
454 
455  g_autoptr(FlValue) null = fl_value_new_null();
456  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
457  ::testing::Eq<FlBinaryMessenger*>(messenger),
458  ::testing::_, SuccessResponse(null), ::testing::_))
459  .WillOnce(::testing::Return(true));
460 
461  messenger.ReceiveMessage("flutter/textinput", set_client);
462 
463  // set editing state
464  g_autoptr(FlValue) state = build_editing_state({
465  .text = "Flutter",
466  .selection_base = 4,
467  .selection_extent = 4,
468  });
469  g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
470  FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
471 
472  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
473  ::testing::Eq<FlBinaryMessenger*>(messenger),
474  ::testing::_, SuccessResponse(null), ::testing::_))
475  .WillOnce(::testing::Return(true));
476 
477  messenger.ReceiveMessage("flutter/textinput", set_state);
478 
479  // move cursor to beginning
480  g_autoptr(FlValue) beginning = build_list({
481  fl_value_new_int(1), // client_id
483  .text = "Flutter",
484  .selection_base = 0,
485  .selection_extent = 0,
486  }),
487  });
488 
489  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
490  ::testing::Eq<FlBinaryMessenger*>(messenger),
491  ::testing::StrEq("flutter/textinput"),
492  MethodCall("TextInputClient.updateEditingState",
493  FlValueEq(beginning)),
494  ::testing::_, ::testing::_, ::testing::_));
495 
496  send_key_event(plugin, GDK_KEY_Home);
497 
498  // move cursor to end
499  g_autoptr(FlValue) end = build_list({
500  fl_value_new_int(1), // client_id
502  .text = "Flutter",
503  .selection_base = 7,
504  .selection_extent = 7,
505  }),
506  });
507 
508  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
509  ::testing::Eq<FlBinaryMessenger*>(messenger),
510  ::testing::StrEq("flutter/textinput"),
511  MethodCall("TextInputClient.updateEditingState",
512  FlValueEq(end)),
513  ::testing::_, ::testing::_, ::testing::_));
514 
515  send_key_event(plugin, GDK_KEY_End);
516 }
517 
518 TEST(FlTextInputPluginTest, Select) {
519  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
520  ::testing::NiceMock<flutter::testing::MockIMContext> context;
521  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
522 
523  g_autoptr(FlTextInputPlugin) plugin =
524  fl_text_input_plugin_new(messenger, context, delegate);
525  EXPECT_NE(plugin, nullptr);
526 
527  // set input config
528  g_autoptr(FlValue) config = build_input_config({.client_id = 1});
529  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
531  FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
532 
533  g_autoptr(FlValue) null = fl_value_new_null();
534  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
535  ::testing::Eq<FlBinaryMessenger*>(messenger),
536  ::testing::_, SuccessResponse(null), ::testing::_))
537  .WillOnce(::testing::Return(true));
538 
539  messenger.ReceiveMessage("flutter/textinput", set_client);
540 
541  // set editing state
542  g_autoptr(FlValue) state = build_editing_state({
543  .text = "Flutter",
544  .selection_base = 4,
545  .selection_extent = 4,
546  });
547  g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
548  FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
549 
550  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
551  ::testing::Eq<FlBinaryMessenger*>(messenger),
552  ::testing::_, SuccessResponse(null), ::testing::_))
553  .WillOnce(::testing::Return(true));
554 
555  messenger.ReceiveMessage("flutter/textinput", set_state);
556 
557  // select to end
558  g_autoptr(FlValue) select_to_end = build_list({
559  fl_value_new_int(1), // client_id
561  .text = "Flutter",
562  .selection_base = 4,
563  .selection_extent = 7,
564  }),
565  });
566 
567  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
568  ::testing::Eq<FlBinaryMessenger*>(messenger),
569  ::testing::StrEq("flutter/textinput"),
570  MethodCall("TextInputClient.updateEditingState",
571  FlValueEq(select_to_end)),
572  ::testing::_, ::testing::_, ::testing::_));
573 
574  send_key_event(plugin, GDK_KEY_End, GDK_SHIFT_MASK);
575 
576  // select to beginning
577  g_autoptr(FlValue) select_to_beginning = build_list({
578  fl_value_new_int(1), // client_id
580  .text = "Flutter",
581  .selection_base = 4,
582  .selection_extent = 0,
583  }),
584  });
585 
586  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
587  ::testing::Eq<FlBinaryMessenger*>(messenger),
588  ::testing::StrEq("flutter/textinput"),
589  MethodCall("TextInputClient.updateEditingState",
590  FlValueEq(select_to_beginning)),
591  ::testing::_, ::testing::_, ::testing::_));
592 
593  send_key_event(plugin, GDK_KEY_Home, GDK_SHIFT_MASK);
594 }
595 
596 TEST(FlTextInputPluginTest, Composing) {
597  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
598  ::testing::NiceMock<flutter::testing::MockIMContext> context;
599  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
600 
601  g_autoptr(FlTextInputPlugin) plugin =
602  fl_text_input_plugin_new(messenger, context, delegate);
603  EXPECT_NE(plugin, nullptr);
604 
605  g_signal_emit_by_name(context, "preedit-start", nullptr);
606 
607  // update
608  EXPECT_CALL(context,
609  gtk_im_context_get_preedit_string(
610  ::testing::Eq<GtkIMContext*>(context),
611  ::testing::A<gchar**>(), ::testing::_, ::testing::A<gint*>()))
612  .WillOnce(
613  ::testing::DoAll(::testing::SetArgPointee<1>(g_strdup("Flutter")),
614  ::testing::SetArgPointee<3>(0)));
615 
616  g_autoptr(FlValue) state = build_list({
617  fl_value_new_int(-1), // client_id
619  .text = "Flutter",
620  .selection_base = 0,
621  .selection_extent = 0,
622  .composing_base = 0,
623  .composing_extent = 7,
624  }),
625  });
626 
627  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
628  ::testing::Eq<FlBinaryMessenger*>(messenger),
629  ::testing::StrEq("flutter/textinput"),
630  MethodCall("TextInputClient.updateEditingState",
631  FlValueEq(state)),
632  ::testing::_, ::testing::_, ::testing::_));
633 
634  g_signal_emit_by_name(context, "preedit-changed", nullptr);
635 
636  // commit
637  g_autoptr(FlValue) commit = build_list({
638  fl_value_new_int(-1), // client_id
640  .text = "engine",
641  .selection_base = 6,
642  .selection_extent = 6,
643  }),
644  });
645 
646  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
647  ::testing::Eq<FlBinaryMessenger*>(messenger),
648  ::testing::StrEq("flutter/textinput"),
649  MethodCall("TextInputClient.updateEditingState",
650  FlValueEq(commit)),
651  ::testing::_, ::testing::_, ::testing::_));
652 
653  g_signal_emit_by_name(context, "commit", "engine", nullptr);
654 
655  // end
656  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
657  ::testing::Eq<FlBinaryMessenger*>(messenger),
658  ::testing::StrEq("flutter/textinput"),
659  MethodCall("TextInputClient.updateEditingState",
660  ::testing::_),
661  ::testing::_, ::testing::_, ::testing::_));
662 
663  g_signal_emit_by_name(context, "preedit-end", nullptr);
664 }
665 
666 TEST(FlTextInputPluginTest, SurroundingText) {
667  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
668  ::testing::NiceMock<flutter::testing::MockIMContext> context;
669  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
670 
671  g_autoptr(FlTextInputPlugin) plugin =
672  fl_text_input_plugin_new(messenger, context, delegate);
673  EXPECT_NE(plugin, nullptr);
674 
675  // set input config
676  g_autoptr(FlValue) config = build_input_config({.client_id = 1});
677  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
679  FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
680 
681  g_autoptr(FlValue) null = fl_value_new_null();
682  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
683  ::testing::Eq<FlBinaryMessenger*>(messenger),
684  ::testing::_, SuccessResponse(null), ::testing::_))
685  .WillOnce(::testing::Return(true));
686 
687  messenger.ReceiveMessage("flutter/textinput", set_client);
688 
689  // set editing state
690  g_autoptr(FlValue) state = build_editing_state({
691  .text = "Flutter",
692  .selection_base = 3,
693  .selection_extent = 3,
694  });
695  g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
696  FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
697 
698  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
699  ::testing::Eq<FlBinaryMessenger*>(messenger),
700  ::testing::_, SuccessResponse(null), ::testing::_))
701  .WillOnce(::testing::Return(true));
702 
703  messenger.ReceiveMessage("flutter/textinput", set_state);
704 
705  // retrieve
706  EXPECT_CALL(context, gtk_im_context_set_surrounding(
707  ::testing::Eq<GtkIMContext*>(context),
708  ::testing::StrEq("Flutter"), 7, 3));
709 
710  gboolean retrieved = false;
711  g_signal_emit_by_name(context, "retrieve-surrounding", &retrieved, nullptr);
712  EXPECT_TRUE(retrieved);
713 
714  // delete
715  g_autoptr(FlValue) update = build_list({
716  fl_value_new_int(1), // client_id
718  .text = "Flutr",
719  .selection_base = 3,
720  .selection_extent = 3,
721  }),
722  });
723 
724  EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
725  ::testing::Eq<FlBinaryMessenger*>(messenger),
726  ::testing::StrEq("flutter/textinput"),
727  MethodCall("TextInputClient.updateEditingState",
728  FlValueEq(update)),
729  ::testing::_, ::testing::_, ::testing::_));
730 
731  gboolean deleted = false;
732  g_signal_emit_by_name(context, "delete-surrounding", 1, 2, &deleted, nullptr);
733  EXPECT_TRUE(deleted);
734 }
735 
736 TEST(FlTextInputPluginTest, SetMarkedTextRect) {
737  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
738  ::testing::NiceMock<flutter::testing::MockIMContext> context;
739  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
740 
741  g_autoptr(FlTextInputPlugin) plugin =
742  fl_text_input_plugin_new(messenger, context, delegate);
743  EXPECT_NE(plugin, nullptr);
744 
745  g_signal_emit_by_name(context, "preedit-start", nullptr);
746 
747  // set editable size and transform
748  g_autoptr(FlValue) size_and_transform = build_map({
749  {
750  "transform",
751  build_list({
761  fl_value_new_float(10),
762  fl_value_new_float(11),
763  fl_value_new_float(12),
764  fl_value_new_float(13),
765  fl_value_new_float(14),
766  fl_value_new_float(15),
767  fl_value_new_float(16),
768  }),
769  },
770  });
771  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
772  g_autoptr(GBytes) set_editable_size_and_transform =
774  FL_METHOD_CODEC(codec), "TextInput.setEditableSizeAndTransform",
775  size_and_transform, nullptr);
776 
777  g_autoptr(FlValue) null = fl_value_new_null();
778  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
779  ::testing::Eq<FlBinaryMessenger*>(messenger),
780  ::testing::_, SuccessResponse(null), ::testing::_))
781  .WillOnce(::testing::Return(true));
782 
783  messenger.ReceiveMessage("flutter/textinput",
785 
786  // set marked text rect
787  g_autoptr(FlValue) rect = build_map({
788  {"x", fl_value_new_float(1)},
789  {"y", fl_value_new_float(2)},
790  {"width", fl_value_new_float(3)},
791  {"height", fl_value_new_float(4)},
792  });
794  FL_METHOD_CODEC(codec), "TextInput.setMarkedTextRect", rect, nullptr);
795 
796  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
797  ::testing::Eq<FlBinaryMessenger*>(messenger),
798  ::testing::_, SuccessResponse(null), ::testing::_))
799  .WillOnce(::testing::Return(true));
800 
802  ::testing::Eq<FlTextInputViewDelegate*>(delegate),
803  ::testing::Eq(27), ::testing::Eq(32), ::testing::_,
804  ::testing::_))
805  .WillOnce(::testing::DoAll(::testing::SetArgPointee<3>(123),
806  ::testing::SetArgPointee<4>(456)));
807 
808  EXPECT_CALL(context, gtk_im_context_set_cursor_location(
809  ::testing::Eq<GtkIMContext*>(context),
810  ::testing::Pointee(::testing::AllOf(
811  ::testing::Field(&GdkRectangle::x, 123),
812  ::testing::Field(&GdkRectangle::y, 456),
813  ::testing::Field(&GdkRectangle::width, 0),
814  ::testing::Field(&GdkRectangle::height, 0)))));
815 
816  messenger.ReceiveMessage("flutter/textinput", set_marked_text_rect);
817 }
818 
819 TEST(FlTextInputPluginTest, TextInputTypeNone) {
820  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
821  ::testing::NiceMock<flutter::testing::MockIMContext> context;
822  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
823 
824  g_autoptr(FlTextInputPlugin) plugin =
825  fl_text_input_plugin_new(messenger, context, delegate);
826  EXPECT_NE(plugin, nullptr);
827 
828  g_autoptr(FlValue) args = build_input_config({
829  .client_id = 1,
830  .input_type = "TextInputType.none",
831  });
832  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
834  FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr);
835 
836  g_autoptr(FlValue) null = fl_value_new_null();
837  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
838  ::testing::Eq<FlBinaryMessenger*>(messenger),
839  ::testing::A<FlBinaryMessengerResponseHandle*>(),
840  SuccessResponse(null), ::testing::A<GError**>()))
841  .WillOnce(::testing::Return(true));
842 
843  messenger.ReceiveMessage("flutter/textinput", set_client);
844 
845  EXPECT_CALL(context,
846  gtk_im_context_focus_in(::testing::Eq<GtkIMContext*>(context)))
847  .Times(0);
848  EXPECT_CALL(context,
849  gtk_im_context_focus_out(::testing::Eq<GtkIMContext*>(context)));
850 
851  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
852  ::testing::Eq<FlBinaryMessenger*>(messenger),
853  ::testing::_, SuccessResponse(null), ::testing::_))
854  .WillOnce(::testing::Return(true));
855 
856  g_autoptr(GBytes) show = fl_method_codec_encode_method_call(
857  FL_METHOD_CODEC(codec), "TextInput.show", nullptr, nullptr);
858 
859  messenger.ReceiveMessage("flutter/textinput", show);
860 }
861 
862 TEST(FlTextInputPluginTest, TextEditingDelta) {
863  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
864  ::testing::NiceMock<flutter::testing::MockIMContext> context;
865  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
866 
867  g_autoptr(FlTextInputPlugin) plugin =
868  fl_text_input_plugin_new(messenger, context, delegate);
869  EXPECT_NE(plugin, nullptr);
870 
871  // set config
872  g_autoptr(FlValue) args = build_input_config({
873  .client_id = 1,
874  .enable_delta_model = true,
875  });
876  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
878  FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr);
879 
880  g_autoptr(FlValue) null = fl_value_new_null();
881  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
882  ::testing::Eq<FlBinaryMessenger*>(messenger),
883  ::testing::A<FlBinaryMessengerResponseHandle*>(),
884  SuccessResponse(null), ::testing::A<GError**>()))
885  .WillOnce(::testing::Return(true));
886 
887  messenger.ReceiveMessage("flutter/textinput", set_client);
888 
889  // set editing state
890  g_autoptr(FlValue) state = build_editing_state({
891  .text = "Flutter",
892  .selection_base = 7,
893  .selection_extent = 7,
894  });
895  g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
896  FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
897 
898  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
899  ::testing::Eq<FlBinaryMessenger*>(messenger),
900  ::testing::_, SuccessResponse(null), ::testing::_))
901  .WillOnce(::testing::Return(true));
902 
903  messenger.ReceiveMessage("flutter/textinput", set_state);
904 
905  // update editing state with deltas
906  g_autoptr(FlValue) deltas = build_list({
907  fl_value_new_int(1), // client_id
908  build_map({{
909  "deltas",
910  build_list({
912  .old_text = "Flutter",
913  .delta_text = "Flutter",
914  .delta_start = 7,
915  .delta_end = 7,
916  .selection_base = 0,
917  .selection_extent = 0,
918  }),
919  }),
920  }}),
921  });
922 
923  EXPECT_CALL(messenger,
925  ::testing::Eq<FlBinaryMessenger*>(messenger),
926  ::testing::StrEq("flutter/textinput"),
927  MethodCall("TextInputClient.updateEditingStateWithDeltas",
928  FlValueEq(deltas)),
929  ::testing::_, ::testing::_, ::testing::_));
930 
931  send_key_event(plugin, GDK_KEY_Home);
932 }
933 
934 TEST(FlTextInputPluginTest, ComposingDelta) {
935  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
936  ::testing::NiceMock<flutter::testing::MockIMContext> context;
937  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
938 
939  g_autoptr(FlTextInputPlugin) plugin =
940  fl_text_input_plugin_new(messenger, context, delegate);
941  EXPECT_NE(plugin, nullptr);
942 
943  // set config
944  g_autoptr(FlValue) args = build_input_config({
945  .client_id = 1,
946  .enable_delta_model = true,
947  });
948  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
950  FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr);
951 
952  g_autoptr(FlValue) null = fl_value_new_null();
953  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
954  ::testing::Eq<FlBinaryMessenger*>(messenger),
955  ::testing::A<FlBinaryMessengerResponseHandle*>(),
956  SuccessResponse(null), ::testing::A<GError**>()))
957  .WillOnce(::testing::Return(true));
958 
959  messenger.ReceiveMessage("flutter/textinput", set_client);
960 
961  g_signal_emit_by_name(context, "preedit-start", nullptr);
962 
963  // update
964  EXPECT_CALL(context,
965  gtk_im_context_get_preedit_string(
966  ::testing::Eq<GtkIMContext*>(context),
967  ::testing::A<gchar**>(), ::testing::_, ::testing::A<gint*>()))
968  .WillOnce(
969  ::testing::DoAll(::testing::SetArgPointee<1>(g_strdup("Flutter ")),
970  ::testing::SetArgPointee<3>(8)));
971 
972  g_autoptr(FlValue) update = build_list({
973  fl_value_new_int(1), // client_id
974  build_map({{
975  "deltas",
976  build_list({
978  .old_text = "",
979  .delta_text = "Flutter ",
980  .delta_start = 0,
981  .delta_end = 0,
982  .selection_base = 8,
983  .selection_extent = 8,
984  .composing_base = 0,
985  .composing_extent = 8,
986  }),
987  }),
988  }}),
989  });
990 
991  EXPECT_CALL(messenger,
993  ::testing::Eq<FlBinaryMessenger*>(messenger),
994  ::testing::StrEq("flutter/textinput"),
995  MethodCall("TextInputClient.updateEditingStateWithDeltas",
996  FlValueEq(update)),
997  ::testing::_, ::testing::_, ::testing::_));
998 
999  g_signal_emit_by_name(context, "preedit-changed", nullptr);
1000 
1001  // commit
1002  g_autoptr(FlValue) commit = build_list({
1003  fl_value_new_int(1), // client_id
1004  build_map({{
1005  "deltas",
1006  build_list({
1008  .old_text = "Flutter ",
1009  .delta_text = "Flutter engine",
1010  .delta_start = 0,
1011  .delta_end = 8,
1012  .selection_base = 14,
1013  .selection_extent = 14,
1014  .composing_base = -1,
1015  .composing_extent = -1,
1016  }),
1017  }),
1018  }}),
1019  });
1020 
1021  EXPECT_CALL(messenger,
1023  ::testing::Eq<FlBinaryMessenger*>(messenger),
1024  ::testing::StrEq("flutter/textinput"),
1025  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1026  FlValueEq(commit)),
1027  ::testing::_, ::testing::_, ::testing::_));
1028 
1029  g_signal_emit_by_name(context, "commit", "Flutter engine", nullptr);
1030 
1031  // end
1032  g_autoptr(FlValue) end = build_list({
1033  fl_value_new_int(1), // client_id
1034  build_map({{
1035  "deltas",
1036  build_list({
1038  .old_text = "Flutter engine",
1039  .selection_base = 14,
1040  .selection_extent = 14,
1041  }),
1042  }),
1043  }}),
1044  });
1045 
1046  EXPECT_CALL(messenger,
1048  ::testing::Eq<FlBinaryMessenger*>(messenger),
1049  ::testing::StrEq("flutter/textinput"),
1050  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1051  FlValueEq(end)),
1052  ::testing::_, ::testing::_, ::testing::_));
1053 
1054  g_signal_emit_by_name(context, "preedit-end", nullptr);
1055 }
1056 
1057 TEST(FlTextInputPluginTest, NonComposingDelta) {
1058  ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
1059  ::testing::NiceMock<flutter::testing::MockIMContext> context;
1060  ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
1061 
1062  g_autoptr(FlTextInputPlugin) plugin =
1063  fl_text_input_plugin_new(messenger, context, delegate);
1064  EXPECT_NE(plugin, nullptr);
1065 
1066  // set config
1067  g_autoptr(FlValue) args = build_input_config({
1068  .client_id = 1,
1069  .enable_delta_model = true,
1070  });
1071  g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
1072  g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
1073  FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr);
1074 
1075  g_autoptr(FlValue) null = fl_value_new_null();
1076  EXPECT_CALL(messenger, fl_binary_messenger_send_response(
1077  ::testing::Eq<FlBinaryMessenger*>(messenger),
1078  ::testing::A<FlBinaryMessengerResponseHandle*>(),
1079  SuccessResponse(null), ::testing::A<GError**>()))
1080  .WillOnce(::testing::Return(true));
1081 
1082  messenger.ReceiveMessage("flutter/textinput", set_client);
1083 
1084  // commit F
1085  g_autoptr(FlValue) commit = build_list({
1086  fl_value_new_int(1), // client_id
1087  build_map({{
1088  "deltas",
1089  build_list({
1091  .old_text = "",
1092  .delta_text = "F",
1093  .delta_start = 0,
1094  .delta_end = 0,
1095  .selection_base = 1,
1096  .selection_extent = 1,
1097  .composing_base = -1,
1098  .composing_extent = -1,
1099  }),
1100  }),
1101  }}),
1102  });
1103 
1104  EXPECT_CALL(messenger,
1106  ::testing::Eq<FlBinaryMessenger*>(messenger),
1107  ::testing::StrEq("flutter/textinput"),
1108  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1109  FlValueEq(commit)),
1110  ::testing::_, ::testing::_, ::testing::_));
1111 
1112  g_signal_emit_by_name(context, "commit", "F", nullptr);
1113 
1114  // commit l
1115  g_autoptr(FlValue) commitL = build_list({
1116  fl_value_new_int(1), // client_id
1117  build_map({{
1118  "deltas",
1119  build_list({
1121  .old_text = "F",
1122  .delta_text = "l",
1123  .delta_start = 1,
1124  .delta_end = 1,
1125  .selection_base = 2,
1126  .selection_extent = 2,
1127  .composing_base = -1,
1128  .composing_extent = -1,
1129  }),
1130  }),
1131  }}),
1132  });
1133 
1134  EXPECT_CALL(messenger,
1136  ::testing::Eq<FlBinaryMessenger*>(messenger),
1137  ::testing::StrEq("flutter/textinput"),
1138  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1139  FlValueEq(commitL)),
1140  ::testing::_, ::testing::_, ::testing::_));
1141 
1142  g_signal_emit_by_name(context, "commit", "l", nullptr);
1143 
1144  // commit u
1145  g_autoptr(FlValue) commitU = build_list({
1146  fl_value_new_int(1), // client_id
1147  build_map({{
1148  "deltas",
1149  build_list({
1151  .old_text = "Fl",
1152  .delta_text = "u",
1153  .delta_start = 2,
1154  .delta_end = 2,
1155  .selection_base = 3,
1156  .selection_extent = 3,
1157  .composing_base = -1,
1158  .composing_extent = -1,
1159  }),
1160  }),
1161  }}),
1162  });
1163 
1164  EXPECT_CALL(messenger,
1166  ::testing::Eq<FlBinaryMessenger*>(messenger),
1167  ::testing::StrEq("flutter/textinput"),
1168  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1169  FlValueEq(commitU)),
1170  ::testing::_, ::testing::_, ::testing::_));
1171 
1172  g_signal_emit_by_name(context, "commit", "u", nullptr);
1173 
1174  // commit t
1175  g_autoptr(FlValue) commitTa = build_list({
1176  fl_value_new_int(1), // client_id
1177  build_map({{
1178  "deltas",
1179  build_list({
1181  .old_text = "Flu",
1182  .delta_text = "t",
1183  .delta_start = 3,
1184  .delta_end = 3,
1185  .selection_base = 4,
1186  .selection_extent = 4,
1187  .composing_base = -1,
1188  .composing_extent = -1,
1189  }),
1190  }),
1191  }}),
1192  });
1193 
1194  EXPECT_CALL(messenger,
1196  ::testing::Eq<FlBinaryMessenger*>(messenger),
1197  ::testing::StrEq("flutter/textinput"),
1198  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1199  FlValueEq(commitTa)),
1200  ::testing::_, ::testing::_, ::testing::_));
1201 
1202  g_signal_emit_by_name(context, "commit", "t", nullptr);
1203 
1204  // commit t again
1205  g_autoptr(FlValue) commitTb = build_list({
1206  fl_value_new_int(1), // client_id
1207  build_map({{
1208  "deltas",
1209  build_list({
1211  .old_text = "Flut",
1212  .delta_text = "t",
1213  .delta_start = 4,
1214  .delta_end = 4,
1215  .selection_base = 5,
1216  .selection_extent = 5,
1217  .composing_base = -1,
1218  .composing_extent = -1,
1219  }),
1220  }),
1221  }}),
1222  });
1223 
1224  EXPECT_CALL(messenger,
1226  ::testing::Eq<FlBinaryMessenger*>(messenger),
1227  ::testing::StrEq("flutter/textinput"),
1228  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1229  FlValueEq(commitTb)),
1230  ::testing::_, ::testing::_, ::testing::_));
1231 
1232  g_signal_emit_by_name(context, "commit", "t", nullptr);
1233 
1234  // commit e
1235  g_autoptr(FlValue) commitE = build_list({
1236  fl_value_new_int(1), // client_id
1237  build_map({{
1238  "deltas",
1239  build_list({
1241  .old_text = "Flutt",
1242  .delta_text = "e",
1243  .delta_start = 5,
1244  .delta_end = 5,
1245  .selection_base = 6,
1246  .selection_extent = 6,
1247  .composing_base = -1,
1248  .composing_extent = -1,
1249  }),
1250  }),
1251  }}),
1252  });
1253 
1254  EXPECT_CALL(messenger,
1256  ::testing::Eq<FlBinaryMessenger*>(messenger),
1257  ::testing::StrEq("flutter/textinput"),
1258  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1259  FlValueEq(commitE)),
1260  ::testing::_, ::testing::_, ::testing::_));
1261 
1262  g_signal_emit_by_name(context, "commit", "e", nullptr);
1263 
1264  // commit r
1265  g_autoptr(FlValue) commitR = build_list({
1266  fl_value_new_int(1), // client_id
1267  build_map({{
1268  "deltas",
1269  build_list({
1271  .old_text = "Flutte",
1272  .delta_text = "r",
1273  .delta_start = 6,
1274  .delta_end = 6,
1275  .selection_base = 7,
1276  .selection_extent = 7,
1277  .composing_base = -1,
1278  .composing_extent = -1,
1279  }),
1280  }),
1281  }}),
1282  });
1283 
1284  EXPECT_CALL(messenger,
1286  ::testing::Eq<FlBinaryMessenger*>(messenger),
1287  ::testing::StrEq("flutter/textinput"),
1288  MethodCall("TextInputClient.updateEditingStateWithDeltas",
1289  FlValueEq(commitR)),
1290  ::testing::_, ::testing::_, ::testing::_));
1291 
1292  g_signal_emit_by_name(context, "commit", "r", nullptr);
1293 }
EditingDelta::delta_end
int delta_end
Definition: fl_text_input_plugin_test.cc:159
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_plugin_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
MATCHER_P
MATCHER_P(SuccessResponse, result, "")
Definition: fl_text_input_plugin_test.cc:27
MethodCallMatcher::MethodCallMatcher
MethodCallMatcher(::testing::Matcher< std::string > name, ::testing::Matcher< FlValue * > args)
Definition: fl_text_input_plugin_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:7
InputConfig::enable_delta_model
gboolean enable_delta_model
Definition: fl_text_input_plugin_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_plugin_test.cc:82
set_editable_size_and_transform
static FlMethodResponse * set_editable_size_and_transform(FlTextInputPlugin *self, FlValue *args)
Definition: fl_text_input_plugin.cc:518
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
fl_text_input_plugin_filter_keypress
gboolean fl_text_input_plugin_filter_keypress(FlTextInputPlugin *self, FlKeyEvent *event)
Definition: fl_text_input_plugin.cc:776
MethodCall
::testing::Matcher< GBytes * > MethodCall(const std::string &name, ::testing::Matcher< FlValue * > args)
Definition: fl_text_input_plugin_test.cc:94
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_plugin_test.cc:163
fl_text_input_plugin.h
set_marked_text_rect
static FlMethodResponse * set_marked_text_rect(FlTextInputPlugin *self, FlValue *args)
Definition: fl_text_input_plugin.cc:542
EditingState::composing_base
int composing_base
Definition: fl_text_input_plugin_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_value_new_null
G_MODULE_EXPORT FlValue * fl_value_new_null()
Definition: fl_value.cc:251
InputConfig::client_id
int64_t client_id
Definition: fl_text_input_plugin_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_plugin_test.cc:157
EditingState::selection_base
int selection_base
Definition: fl_text_input_plugin_test.cc:137
EditingDelta::delta_start
int delta_start
Definition: fl_text_input_plugin_test.cc:158
height
G_BEGIN_DECLS int height
Definition: fl_backing_store_provider.h:37
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_plugin_test.cc:161
_FlKeyEvent
Definition: fl_key_event.h:22
state
AtkStateType state
Definition: fl_accessible_node.cc:10
set_client
static FlMethodResponse * set_client(FlTextInputPlugin *self, FlValue *args)
Definition: fl_text_input_plugin.cc:364
update
G_BEGIN_DECLS const FlutterSemanticsUpdate2 * update
Definition: fl_view_accessible.h:40
show
static FlMethodResponse * show(FlTextInputPlugin *self)
Definition: fl_text_input_plugin.cc:417
build_editing_state
static FlValue * build_editing_state(EditingState state)
Definition: fl_text_input_plugin_test.cc:143
TEST
TEST(FlTextInputPluginTest, MessageHandler)
Definition: fl_text_input_plugin_test.cc:192
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_plugin_test.cc:136
build_map
static FlValue * build_map(std::map< const gchar *, FlValue * > args)
Definition: fl_text_input_plugin_test.cc:99
MethodCallMatcher::DescribeTo
void DescribeTo(std::ostream *os) const
Definition: fl_text_input_plugin_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_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_plugin_test.cc:156
MethodCallMatcher::is_gtest_matcher
void is_gtest_matcher
Definition: fl_platform_plugin_test.cc:33
EditingState
Definition: fl_text_input_plugin_test.cc:135
printTo
void printTo(FlMethodResponse *response, ::std::ostream *os)
Definition: fl_text_input_plugin_test.cc:22
build_input_config
static FlValue * build_input_config(InputConfig config)
Definition: fl_text_input_plugin_test.cc:122
build_list
static FlValue * build_list(std::vector< FlValue * > args)
Definition: fl_text_input_plugin_test.cc:107
EditingState::composing_extent
int composing_extent
Definition: fl_text_input_plugin_test.cc:140
end
glong glong end
Definition: fl_accessible_text_field.cc:40
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_plugin_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_plugin_test.cc:115
EditingDelta::composing_base
int composing_base
Definition: fl_text_input_plugin_test.cc:162
fl_key_event_dispose
void fl_key_event_dispose(FlKeyEvent *event)
Definition: fl_key_event.cc:32
result
GAsyncResult * result
Definition: fl_text_input_plugin.cc:106
MethodCallMatcher
Definition: fl_platform_plugin_test.cc:31
build_editing_delta
static FlValue * build_editing_delta(EditingDelta delta)
Definition: fl_text_input_plugin_test.cc:166
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
InputConfig::input_type
const gchar * input_type
Definition: fl_text_input_plugin_test.cc:117
EditingState::selection_extent
int selection_extent
Definition: fl_text_input_plugin_test.cc:138
send_key_event
static void send_key_event(FlTextInputPlugin *plugin, gint keyval, gint state=0)
Definition: fl_text_input_plugin_test.cc:181
InputConfig::input_action
const gchar * input_action
Definition: fl_text_input_plugin_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:438
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
value
uint8_t value
Definition: fl_standard_message_codec.cc:36
fl_text_input_plugin_new
FlTextInputPlugin * fl_text_input_plugin_new(FlBinaryMessenger *messenger, GtkIMContext *im_context, FlTextInputViewDelegate *view_delegate)
Definition: fl_text_input_plugin.cc:745
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:451
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_plugin_test.cc:160