| 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
7 #include "flutter/shell/platform/linux/fl_method_codec_private.h"
8 #include "flutter/shell/platform/linux/fl_text_input_plugin.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(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();
315 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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
345 build_editing_state({
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();
392 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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();
452 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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
482 build_editing_state({
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
501 build_editing_state({
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();
530 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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
560 build_editing_state({
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
579 build_editing_state({
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
618 build_editing_state({
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
639 build_editing_state({
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();
678 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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
717 build_editing_state({
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({
752 fl_value_new_float(1),
753 fl_value_new_float(2),
754 fl_value_new_float(3),
755 fl_value_new_float(4),
756 fl_value_new_float(5),
757 fl_value_new_float(6),
758 fl_value_new_float(7),
759 fl_value_new_float(8),
760 fl_value_new_float(9),
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 =
773 fl_method_codec_encode_method_call(
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",
784 set_editable_size_and_transform);
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 });
793 g_autoptr(GBytes) set_marked_text_rect = fl_method_codec_encode_method_call(
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
801 EXPECT_CALL(delegate, fl_text_input_view_delegate_translate_coordinates(
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();
833 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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();
877 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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({
911 build_editing_delta({
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,
924 fl_binary_messenger_send_on_channel(
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();
949 g_autoptr(GBytes) set_client = fl_method_codec_encode_method_call(
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({
977 build_editing_delta({
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,
992 fl_binary_messenger_send_on_channel(
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({
1007 build_editing_delta({
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,
1022 fl_binary_messenger_send_on_channel(
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({
1037 build_editing_delta({
1038 .old_text = "Flutter engine",
1039 .selection_base = 14,
1040 .selection_extent = 14,
1041 }),
1042 }),
1043 }}),
1044 });
1045
1046 EXPECT_CALL(messenger,
1047 fl_binary_messenger_send_on_channel(
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({
1090 build_editing_delta({
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,
1105 fl_binary_messenger_send_on_channel(
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({
1120 build_editing_delta({
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,
1135 fl_binary_messenger_send_on_channel(
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({
1150 build_editing_delta({
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,
1165 fl_binary_messenger_send_on_channel(
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({
1180 build_editing_delta({
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,
1195 fl_binary_messenger_send_on_channel(
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({
1210 build_editing_delta({
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,
1225 fl_binary_messenger_send_on_channel(
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({
1240 build_editing_delta({
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,
1255 fl_binary_messenger_send_on_channel(
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({
1270 build_editing_delta({
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,
1285 fl_binary_messenger_send_on_channel(
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
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:610
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:338
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:244
FlValue
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition: fl_value.h:40
fl_value_new_null
G_MODULE_EXPORT FlValue * fl_value_new_null()
Definition: fl_value.cc:240
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_key_event_new_from_gdk_event
FlKeyEvent * fl_key_event_new_from_gdk_event(GdkEvent *raw_event)
Definition: fl_key_event.cc:12
fl_value_new_int
G_MODULE_EXPORT FlValue * fl_value_new_int(int64_t value)
Definition: fl_value.cc:251
EditingDelta::selection_extent
int selection_extent
Definition: fl_text_input_plugin_test.cc:161
_FlKeyEvent
Definition: fl_key_event.h:31
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
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:355
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:31
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:560
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:433
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:33
result
GAsyncResult * result
Definition: fl_text_input_plugin.cc:106
MethodCallMatcher
Definition: fl_platform_plugin_test.cc:29
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:258
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:31
fl_method_codec_private.h
value
uint8_t value
Definition: fl_standard_message_codec.cc:41
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:265
EditingDelta::selection_base
int selection_base
Definition: fl_text_input_plugin_test.cc:160