| 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
7 #include "flutter/shell/platform/linux/fl_method_codec_private.h"
8 #include "flutter/shell/platform/linux/fl_text_input_handler.h"
9 #include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"
10 #include "flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h"
11 #include "flutter/shell/platform/linux/public/flutter_linux/fl_value.h"
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
51 bool MatchAndExplain(GBytes* method_call,
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;
57 gboolean result = fl_method_codec_decode_method_call(
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) {
100 FlValue* value = fl_value_new_map();
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) {
108 FlValue* value = fl_value_new_list();
109 for (auto it = args.begin(); it != args.end(); ++it) {
110 fl_value_append_take(value, *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
122 static FlValue* build_input_config(InputConfig config) {
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;
138 int selection_extent = -1;
139 int composing_base = -1;
140 int composing_extent = -1;
141 };
142
143 static FlValue* build_editing_state(EditingState state) {
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;
161 int selection_extent = -1;
162 int composing_base = -1;
163 int composing_extent = -1;
164 };
165
166 static FlValue* build_editing_delta(EditingDelta delta) {
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();
314 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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
344 build_editing_state({
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();
391 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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();
451 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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
481 build_editing_state({
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
500 build_editing_state({
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();
529 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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
559 build_editing_state({
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
578 build_editing_state({
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
617 build_editing_state({
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
638 build_editing_state({
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();
677 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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
716 build_editing_state({
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({
751 fl_value_new_float(1),
752 fl_value_new_float(2),
753 fl_value_new_float(3),
754 fl_value_new_float(4),
755 fl_value_new_float(5),
756 fl_value_new_float(6),
757 fl_value_new_float(7),
758 fl_value_new_float(8),
759 fl_value_new_float(9),
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 =
772 fl_method_codec_encode_method_call(
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",
783 set_editable_size_and_transform);
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 });
792 g_autoptr(GBytes) set_marked_text_rect = fl_method_codec_encode_method_call(
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
800 EXPECT_CALL(delegate, fl_text_input_view_delegate_translate_coordinates(
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();
832 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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();
876 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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({
910 build_editing_delta({
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,
923 fl_binary_messenger_send_on_channel(
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();
948 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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({
976 build_editing_delta({
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,
991 fl_binary_messenger_send_on_channel(
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({
1006 build_editing_delta({
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,
1021 fl_binary_messenger_send_on_channel(
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({
1036 build_editing_delta({
1037 .old_text = "Flutter engine",
1038 .selection_base = 14,
1039 .selection_extent = 14,
1040 }),
1041 }),
1042 }}),
1043 });
1044
1045 EXPECT_CALL(messenger,
1046 fl_binary_messenger_send_on_channel(
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({
1089 build_editing_delta({
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,
1104 fl_binary_messenger_send_on_channel(
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({
1119 build_editing_delta({
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,
1134 fl_binary_messenger_send_on_channel(
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({
1149 build_editing_delta({
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,
1164 fl_binary_messenger_send_on_channel(
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({
1179 build_editing_delta({
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,
1194 fl_binary_messenger_send_on_channel(
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({
1209 build_editing_delta({
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,
1224 fl_binary_messenger_send_on_channel(
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({
1239 build_editing_delta({
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,
1254 fl_binary_messenger_send_on_channel(
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({
1269 build_editing_delta({
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,
1284 fl_binary_messenger_send_on_channel(
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