Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
22void printTo(FlMethodResponse* response, ::std::ostream* os) {
23 *os << ::testing::PrintToString(
24 fl_method_response_get_result(response, nullptr));
25}
26
27MATCHER_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
39MATCHER_P(FlValueEq, value, "equal to " + ::testing::PrintToString(value)) {
40 return fl_value_equal(arg, value);
41}
42
44 public:
45 using is_gtest_matcher = void;
46
47 explicit MethodCallMatcher(::testing::Matcher<std::string> name,
48 ::testing::Matcher<FlValue*> args)
49 : name_(std::move(name)), args_(std::move(args)) {}
50
52 ::testing::MatchResultListener* result_listener) const {
53 g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
54 g_autoptr(GError) error = nullptr;
55 g_autofree gchar* name = nullptr;
56 g_autoptr(FlValue) args = nullptr;
58 FL_METHOD_CODEC(codec), method_call, &name, &args, &error);
59 if (!result) {
60 *result_listener << ::testing::PrintToString(error->message);
61 return false;
62 }
63 if (!name_.MatchAndExplain(name, result_listener)) {
64 *result_listener << " where the name doesn't match: \"" << name << "\"";
65 return false;
66 }
67 if (!args_.MatchAndExplain(args, result_listener)) {
68 *result_listener << " where the args don't match: "
69 << ::testing::PrintToString(args);
70 return false;
71 }
72 return true;
73 }
74
75 void DescribeTo(std::ostream* os) const {
76 *os << "method name ";
77 name_.DescribeTo(os);
78 *os << " and args ";
79 args_.DescribeTo(os);
80 }
81
82 void DescribeNegationTo(std::ostream* os) const {
83 *os << "method name ";
84 name_.DescribeNegationTo(os);
85 *os << " or args ";
86 args_.DescribeNegationTo(os);
87 }
88
89 private:
90 ::testing::Matcher<std::string> name_;
91 ::testing::Matcher<FlValue*> args_;
92};
93
94::testing::Matcher<GBytes*> MethodCall(const std::string& name,
95 ::testing::Matcher<FlValue*> args) {
96 return MethodCallMatcher(::testing::StrEq(name), std::move(args));
97}
98
99static FlValue* build_map(std::map<const gchar*, FlValue*> args) {
101 for (auto it = args.begin(); it != args.end(); ++it) {
102 fl_value_set_string_take(value, it->first, it->second);
103 }
104 return value;
105}
106
107static FlValue* build_list(std::vector<FlValue*> args) {
109 for (auto it = args.begin(); it != args.end(); ++it) {
111 }
112 return value;
113}
114
116 int64_t client_id = -1;
117 const gchar* input_type = "TextInputType.text";
118 const gchar* input_action = "TextInputAction.none";
119 gboolean enable_delta_model = false;
120};
121
123 return build_list({
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
136 const gchar* text = "";
141};
142
144 return build_map({
145 {"text", fl_value_new_string(state.text)},
146 {"selectionBase", fl_value_new_int(state.selection_base)},
147 {"selectionExtent", fl_value_new_int(state.selection_extent)},
148 {"selectionAffinity", fl_value_new_string("TextAffinity.downstream")},
149 {"selectionIsDirectional", fl_value_new_bool(false)},
150 {"composingBase", fl_value_new_int(state.composing_base)},
151 {"composingExtent", fl_value_new_int(state.composing_extent)},
152 });
153}
154
156 const gchar* old_text = "";
157 const gchar* delta_text = "";
158 int delta_start = -1;
159 int delta_end = -1;
164};
165
167 return build_map({
168 {"oldText", fl_value_new_string(delta.old_text)},
169 {"deltaText", fl_value_new_string(delta.delta_text)},
170 {"deltaStart", fl_value_new_int(delta.delta_start)},
171 {"deltaEnd", fl_value_new_int(delta.delta_end)},
172 {"selectionBase", fl_value_new_int(delta.selection_base)},
173 {"selectionExtent", fl_value_new_int(delta.selection_extent)},
174 {"selectionAffinity", fl_value_new_string("TextAffinity.downstream")},
175 {"selectionIsDirectional", fl_value_new_bool(false)},
176 {"composingBase", fl_value_new_int(delta.composing_base)},
177 {"composingExtent", fl_value_new_int(delta.composing_extent)},
178 });
179}
180
181static 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;
190}
191
192TEST(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
204TEST(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();
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
227TEST(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();
247 FL_METHOD_CODEC(codec), "TextInput.show", nullptr, nullptr);
248
249 messenger.ReceiveMessage("flutter/textinput", message);
250}
251
252TEST(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();
272 FL_METHOD_CODEC(codec), "TextInput.hide", nullptr, nullptr);
273
274 messenger.ReceiveMessage("flutter/textinput", message);
275}
276
277TEST(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();
294 FL_METHOD_CODEC(codec), "TextInput.clearClient", nullptr, nullptr);
295
296 messenger.ReceiveMessage("flutter/textinput", message);
297}
298
299TEST(FlTextInputPluginTest, PerformAction) {
300 ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
301 ::testing::NiceMock<flutter::testing::MockIMContext> context;
302 ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
303
304 g_autoptr(FlTextInputPlugin) plugin =
305 fl_text_input_plugin_new(messenger, context, delegate);
306 EXPECT_NE(plugin, nullptr);
307
308 // set input config
309 g_autoptr(FlValue) config = build_input_config({
310 .client_id = 1,
311 .input_type = "TextInputType.multiline",
312 .input_action = "TextInputAction.newline",
313 });
314 g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
316 FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
317
318 g_autoptr(FlValue) null = fl_value_new_null();
319 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
320 ::testing::Eq<FlBinaryMessenger*>(messenger),
321 ::testing::_, SuccessResponse(null), ::testing::_))
322 .WillOnce(::testing::Return(true));
323
324 messenger.ReceiveMessage("flutter/textinput", set_client);
325
326 // set editing state
327 g_autoptr(FlValue) state = build_editing_state({
328 .text = "Flutter",
329 .selection_base = 7,
330 .selection_extent = 7,
331 });
332 g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
333 FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
334
335 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
336 ::testing::Eq<FlBinaryMessenger*>(messenger),
337 ::testing::_, SuccessResponse(null), ::testing::_))
338 .WillOnce(::testing::Return(true));
339
340 messenger.ReceiveMessage("flutter/textinput", set_state);
341
342 // update editing state
343 g_autoptr(FlValue) new_state = build_list({
344 fl_value_new_int(1), // client_id
346 .text = "Flutter\n",
347 .selection_base = 8,
348 .selection_extent = 8,
349 }),
350 });
351
352 EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
353 ::testing::Eq<FlBinaryMessenger*>(messenger),
354 ::testing::StrEq("flutter/textinput"),
355 MethodCall("TextInputClient.updateEditingState",
356 FlValueEq(new_state)),
357 ::testing::_, ::testing::_, ::testing::_));
358
359 // perform action
360 g_autoptr(FlValue) action = build_list({
361 fl_value_new_int(1), // client_id
362 fl_value_new_string("TextInputAction.newline"),
363 });
364
365 EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
366 ::testing::Eq<FlBinaryMessenger*>(messenger),
367 ::testing::StrEq("flutter/textinput"),
368 MethodCall("TextInputClient.performAction",
369 FlValueEq(action)),
370 ::testing::_, ::testing::_, ::testing::_));
371
372 send_key_event(plugin, GDK_KEY_Return);
373}
374
375// Regression test for https://github.com/flutter/flutter/issues/125879.
376TEST(FlTextInputPluginTest, MultilineWithSendAction) {
377 ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
378 ::testing::NiceMock<flutter::testing::MockIMContext> context;
379 ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
380
381 g_autoptr(FlTextInputPlugin) plugin =
382 fl_text_input_plugin_new(messenger, context, delegate);
383 EXPECT_NE(plugin, nullptr);
384
385 // Set input config.
386 g_autoptr(FlValue) config = build_input_config({
387 .client_id = 1,
388 .input_type = "TextInputType.multiline",
389 .input_action = "TextInputAction.send",
390 });
391 g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
393 FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
394
395 g_autoptr(FlValue) null = fl_value_new_null();
396 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
397 ::testing::Eq<FlBinaryMessenger*>(messenger),
398 ::testing::_, SuccessResponse(null), ::testing::_))
399 .WillOnce(::testing::Return(true));
400
401 messenger.ReceiveMessage("flutter/textinput", set_client);
402
403 // Set editing state.
404 g_autoptr(FlValue) state = build_editing_state({
405 .text = "Flutter",
406 .selection_base = 7,
407 .selection_extent = 7,
408 });
409 g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
410 FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
411
412 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
413 ::testing::Eq<FlBinaryMessenger*>(messenger),
414 ::testing::_, SuccessResponse(null), ::testing::_))
415 .WillOnce(::testing::Return(true));
416
417 messenger.ReceiveMessage("flutter/textinput", set_state);
418
419 // Perform action.
420 g_autoptr(FlValue) action = build_list({
421 fl_value_new_int(1), // client_id
422 fl_value_new_string("TextInputAction.send"),
423 });
424
425 // Because the input action is not set to TextInputAction.newline, the next
426 // expected call is "TextInputClient.performAction". If the input action was
427 // set to TextInputAction.newline the next call would be
428 // "TextInputClient.updateEditingState" (this case is tested in the test named
429 // 'PerformAction').
430 EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
431 ::testing::Eq<FlBinaryMessenger*>(messenger),
432 ::testing::StrEq("flutter/textinput"),
433 MethodCall("TextInputClient.performAction",
434 FlValueEq(action)),
435 ::testing::_, ::testing::_, ::testing::_));
436
437 send_key_event(plugin, GDK_KEY_Return);
438}
439
440TEST(FlTextInputPluginTest, MoveCursor) {
441 ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
442 ::testing::NiceMock<flutter::testing::MockIMContext> context;
443 ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
444
445 g_autoptr(FlTextInputPlugin) plugin =
446 fl_text_input_plugin_new(messenger, context, delegate);
447 EXPECT_NE(plugin, nullptr);
448
449 // set input config
450 g_autoptr(FlValue) config = build_input_config({.client_id = 1});
451 g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
453 FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
454
455 g_autoptr(FlValue) null = fl_value_new_null();
456 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
457 ::testing::Eq<FlBinaryMessenger*>(messenger),
458 ::testing::_, SuccessResponse(null), ::testing::_))
459 .WillOnce(::testing::Return(true));
460
461 messenger.ReceiveMessage("flutter/textinput", set_client);
462
463 // set editing state
464 g_autoptr(FlValue) state = build_editing_state({
465 .text = "Flutter",
466 .selection_base = 4,
467 .selection_extent = 4,
468 });
469 g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
470 FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
471
472 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
473 ::testing::Eq<FlBinaryMessenger*>(messenger),
474 ::testing::_, SuccessResponse(null), ::testing::_))
475 .WillOnce(::testing::Return(true));
476
477 messenger.ReceiveMessage("flutter/textinput", set_state);
478
479 // move cursor to beginning
480 g_autoptr(FlValue) beginning = build_list({
481 fl_value_new_int(1), // client_id
483 .text = "Flutter",
484 .selection_base = 0,
485 .selection_extent = 0,
486 }),
487 });
488
489 EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
490 ::testing::Eq<FlBinaryMessenger*>(messenger),
491 ::testing::StrEq("flutter/textinput"),
492 MethodCall("TextInputClient.updateEditingState",
493 FlValueEq(beginning)),
494 ::testing::_, ::testing::_, ::testing::_));
495
496 send_key_event(plugin, GDK_KEY_Home);
497
498 // move cursor to end
499 g_autoptr(FlValue) end = build_list({
500 fl_value_new_int(1), // client_id
502 .text = "Flutter",
503 .selection_base = 7,
504 .selection_extent = 7,
505 }),
506 });
507
508 EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
509 ::testing::Eq<FlBinaryMessenger*>(messenger),
510 ::testing::StrEq("flutter/textinput"),
511 MethodCall("TextInputClient.updateEditingState",
512 FlValueEq(end)),
513 ::testing::_, ::testing::_, ::testing::_));
514
515 send_key_event(plugin, GDK_KEY_End);
516}
517
518TEST(FlTextInputPluginTest, Select) {
519 ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
520 ::testing::NiceMock<flutter::testing::MockIMContext> context;
521 ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
522
523 g_autoptr(FlTextInputPlugin) plugin =
524 fl_text_input_plugin_new(messenger, context, delegate);
525 EXPECT_NE(plugin, nullptr);
526
527 // set input config
528 g_autoptr(FlValue) config = build_input_config({.client_id = 1});
529 g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
531 FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
532
533 g_autoptr(FlValue) null = fl_value_new_null();
534 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
535 ::testing::Eq<FlBinaryMessenger*>(messenger),
536 ::testing::_, SuccessResponse(null), ::testing::_))
537 .WillOnce(::testing::Return(true));
538
539 messenger.ReceiveMessage("flutter/textinput", set_client);
540
541 // set editing state
542 g_autoptr(FlValue) state = build_editing_state({
543 .text = "Flutter",
544 .selection_base = 4,
545 .selection_extent = 4,
546 });
547 g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
548 FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
549
550 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
551 ::testing::Eq<FlBinaryMessenger*>(messenger),
552 ::testing::_, SuccessResponse(null), ::testing::_))
553 .WillOnce(::testing::Return(true));
554
555 messenger.ReceiveMessage("flutter/textinput", set_state);
556
557 // select to end
558 g_autoptr(FlValue) select_to_end = build_list({
559 fl_value_new_int(1), // client_id
561 .text = "Flutter",
562 .selection_base = 4,
563 .selection_extent = 7,
564 }),
565 });
566
567 EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
568 ::testing::Eq<FlBinaryMessenger*>(messenger),
569 ::testing::StrEq("flutter/textinput"),
570 MethodCall("TextInputClient.updateEditingState",
571 FlValueEq(select_to_end)),
572 ::testing::_, ::testing::_, ::testing::_));
573
574 send_key_event(plugin, GDK_KEY_End, GDK_SHIFT_MASK);
575
576 // select to beginning
577 g_autoptr(FlValue) select_to_beginning = build_list({
578 fl_value_new_int(1), // client_id
580 .text = "Flutter",
581 .selection_base = 4,
582 .selection_extent = 0,
583 }),
584 });
585
586 EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
587 ::testing::Eq<FlBinaryMessenger*>(messenger),
588 ::testing::StrEq("flutter/textinput"),
589 MethodCall("TextInputClient.updateEditingState",
590 FlValueEq(select_to_beginning)),
591 ::testing::_, ::testing::_, ::testing::_));
592
593 send_key_event(plugin, GDK_KEY_Home, GDK_SHIFT_MASK);
594}
595
596TEST(FlTextInputPluginTest, Composing) {
597 ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
598 ::testing::NiceMock<flutter::testing::MockIMContext> context;
599 ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
600
601 g_autoptr(FlTextInputPlugin) plugin =
602 fl_text_input_plugin_new(messenger, context, delegate);
603 EXPECT_NE(plugin, nullptr);
604
605 g_signal_emit_by_name(context, "preedit-start", nullptr);
606
607 // update
608 EXPECT_CALL(context,
609 gtk_im_context_get_preedit_string(
610 ::testing::Eq<GtkIMContext*>(context),
611 ::testing::A<gchar**>(), ::testing::_, ::testing::A<gint*>()))
612 .WillOnce(
613 ::testing::DoAll(::testing::SetArgPointee<1>(g_strdup("Flutter")),
614 ::testing::SetArgPointee<3>(0)));
615
616 g_autoptr(FlValue) state = build_list({
617 fl_value_new_int(-1), // client_id
619 .text = "Flutter",
620 .selection_base = 0,
621 .selection_extent = 0,
622 .composing_base = 0,
623 .composing_extent = 7,
624 }),
625 });
626
627 EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
628 ::testing::Eq<FlBinaryMessenger*>(messenger),
629 ::testing::StrEq("flutter/textinput"),
630 MethodCall("TextInputClient.updateEditingState",
631 FlValueEq(state)),
632 ::testing::_, ::testing::_, ::testing::_));
633
634 g_signal_emit_by_name(context, "preedit-changed", nullptr);
635
636 // commit
637 g_autoptr(FlValue) commit = build_list({
638 fl_value_new_int(-1), // client_id
640 .text = "engine",
641 .selection_base = 6,
642 .selection_extent = 6,
643 }),
644 });
645
646 EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
647 ::testing::Eq<FlBinaryMessenger*>(messenger),
648 ::testing::StrEq("flutter/textinput"),
649 MethodCall("TextInputClient.updateEditingState",
650 FlValueEq(commit)),
651 ::testing::_, ::testing::_, ::testing::_));
652
653 g_signal_emit_by_name(context, "commit", "engine", nullptr);
654
655 // end
656 EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
657 ::testing::Eq<FlBinaryMessenger*>(messenger),
658 ::testing::StrEq("flutter/textinput"),
659 MethodCall("TextInputClient.updateEditingState",
660 ::testing::_),
661 ::testing::_, ::testing::_, ::testing::_));
662
663 g_signal_emit_by_name(context, "preedit-end", nullptr);
664}
665
666TEST(FlTextInputPluginTest, SurroundingText) {
667 ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
668 ::testing::NiceMock<flutter::testing::MockIMContext> context;
669 ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
670
671 g_autoptr(FlTextInputPlugin) plugin =
672 fl_text_input_plugin_new(messenger, context, delegate);
673 EXPECT_NE(plugin, nullptr);
674
675 // set input config
676 g_autoptr(FlValue) config = build_input_config({.client_id = 1});
677 g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
679 FL_METHOD_CODEC(codec), "TextInput.setClient", config, nullptr);
680
681 g_autoptr(FlValue) null = fl_value_new_null();
682 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
683 ::testing::Eq<FlBinaryMessenger*>(messenger),
684 ::testing::_, SuccessResponse(null), ::testing::_))
685 .WillOnce(::testing::Return(true));
686
687 messenger.ReceiveMessage("flutter/textinput", set_client);
688
689 // set editing state
690 g_autoptr(FlValue) state = build_editing_state({
691 .text = "Flutter",
692 .selection_base = 3,
693 .selection_extent = 3,
694 });
695 g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
696 FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
697
698 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
699 ::testing::Eq<FlBinaryMessenger*>(messenger),
700 ::testing::_, SuccessResponse(null), ::testing::_))
701 .WillOnce(::testing::Return(true));
702
703 messenger.ReceiveMessage("flutter/textinput", set_state);
704
705 // retrieve
706 EXPECT_CALL(context, gtk_im_context_set_surrounding(
707 ::testing::Eq<GtkIMContext*>(context),
708 ::testing::StrEq("Flutter"), 7, 3));
709
710 gboolean retrieved = false;
711 g_signal_emit_by_name(context, "retrieve-surrounding", &retrieved, nullptr);
712 EXPECT_TRUE(retrieved);
713
714 // delete
715 g_autoptr(FlValue) update = build_list({
716 fl_value_new_int(1), // client_id
718 .text = "Flutr",
719 .selection_base = 3,
720 .selection_extent = 3,
721 }),
722 });
723
724 EXPECT_CALL(messenger, fl_binary_messenger_send_on_channel(
725 ::testing::Eq<FlBinaryMessenger*>(messenger),
726 ::testing::StrEq("flutter/textinput"),
727 MethodCall("TextInputClient.updateEditingState",
728 FlValueEq(update)),
729 ::testing::_, ::testing::_, ::testing::_));
730
731 gboolean deleted = false;
732 g_signal_emit_by_name(context, "delete-surrounding", 1, 2, &deleted, nullptr);
733 EXPECT_TRUE(deleted);
734}
735
736TEST(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({
768 }),
769 },
770 });
771 g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
772 g_autoptr(GBytes) set_editable_size_and_transform =
774 FL_METHOD_CODEC(codec), "TextInput.setEditableSizeAndTransform",
775 size_and_transform, nullptr);
776
777 g_autoptr(FlValue) null = fl_value_new_null();
778 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
779 ::testing::Eq<FlBinaryMessenger*>(messenger),
780 ::testing::_, SuccessResponse(null), ::testing::_))
781 .WillOnce(::testing::Return(true));
782
783 messenger.ReceiveMessage("flutter/textinput",
785
786 // set marked text rect
787 g_autoptr(FlValue) rect = build_map({
788 {"x", fl_value_new_float(1)},
789 {"y", fl_value_new_float(2)},
790 {"width", fl_value_new_float(3)},
791 {"height", fl_value_new_float(4)},
792 });
794 FL_METHOD_CODEC(codec), "TextInput.setMarkedTextRect", rect, nullptr);
795
796 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
797 ::testing::Eq<FlBinaryMessenger*>(messenger),
798 ::testing::_, SuccessResponse(null), ::testing::_))
799 .WillOnce(::testing::Return(true));
800
802 ::testing::Eq<FlTextInputViewDelegate*>(delegate),
803 ::testing::Eq(27), ::testing::Eq(32), ::testing::_,
804 ::testing::_))
805 .WillOnce(::testing::DoAll(::testing::SetArgPointee<3>(123),
806 ::testing::SetArgPointee<4>(456)));
807
808 EXPECT_CALL(context, gtk_im_context_set_cursor_location(
809 ::testing::Eq<GtkIMContext*>(context),
810 ::testing::Pointee(::testing::AllOf(
811 ::testing::Field(&GdkRectangle::x, 123),
812 ::testing::Field(&GdkRectangle::y, 456),
813 ::testing::Field(&GdkRectangle::width, 0),
814 ::testing::Field(&GdkRectangle::height, 0)))));
815
816 messenger.ReceiveMessage("flutter/textinput", set_marked_text_rect);
817}
818
819TEST(FlTextInputPluginTest, TextInputTypeNone) {
820 ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
821 ::testing::NiceMock<flutter::testing::MockIMContext> context;
822 ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
823
824 g_autoptr(FlTextInputPlugin) plugin =
825 fl_text_input_plugin_new(messenger, context, delegate);
826 EXPECT_NE(plugin, nullptr);
827
828 g_autoptr(FlValue) args = build_input_config({
829 .client_id = 1,
830 .input_type = "TextInputType.none",
831 });
832 g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
834 FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr);
835
836 g_autoptr(FlValue) null = fl_value_new_null();
837 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
838 ::testing::Eq<FlBinaryMessenger*>(messenger),
839 ::testing::A<FlBinaryMessengerResponseHandle*>(),
840 SuccessResponse(null), ::testing::A<GError**>()))
841 .WillOnce(::testing::Return(true));
842
843 messenger.ReceiveMessage("flutter/textinput", set_client);
844
845 EXPECT_CALL(context,
846 gtk_im_context_focus_in(::testing::Eq<GtkIMContext*>(context)))
847 .Times(0);
848 EXPECT_CALL(context,
849 gtk_im_context_focus_out(::testing::Eq<GtkIMContext*>(context)));
850
851 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
852 ::testing::Eq<FlBinaryMessenger*>(messenger),
853 ::testing::_, SuccessResponse(null), ::testing::_))
854 .WillOnce(::testing::Return(true));
855
856 g_autoptr(GBytes) show = fl_method_codec_encode_method_call(
857 FL_METHOD_CODEC(codec), "TextInput.show", nullptr, nullptr);
858
859 messenger.ReceiveMessage("flutter/textinput", show);
860}
861
862TEST(FlTextInputPluginTest, TextEditingDelta) {
863 ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
864 ::testing::NiceMock<flutter::testing::MockIMContext> context;
865 ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
866
867 g_autoptr(FlTextInputPlugin) plugin =
868 fl_text_input_plugin_new(messenger, context, delegate);
869 EXPECT_NE(plugin, nullptr);
870
871 // set config
872 g_autoptr(FlValue) args = build_input_config({
873 .client_id = 1,
874 .enable_delta_model = true,
875 });
876 g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
878 FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr);
879
880 g_autoptr(FlValue) null = fl_value_new_null();
881 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
882 ::testing::Eq<FlBinaryMessenger*>(messenger),
883 ::testing::A<FlBinaryMessengerResponseHandle*>(),
884 SuccessResponse(null), ::testing::A<GError**>()))
885 .WillOnce(::testing::Return(true));
886
887 messenger.ReceiveMessage("flutter/textinput", set_client);
888
889 // set editing state
890 g_autoptr(FlValue) state = build_editing_state({
891 .text = "Flutter",
892 .selection_base = 7,
893 .selection_extent = 7,
894 });
895 g_autoptr(GBytes) set_state = fl_method_codec_encode_method_call(
896 FL_METHOD_CODEC(codec), "TextInput.setEditingState", state, nullptr);
897
898 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
899 ::testing::Eq<FlBinaryMessenger*>(messenger),
900 ::testing::_, SuccessResponse(null), ::testing::_))
901 .WillOnce(::testing::Return(true));
902
903 messenger.ReceiveMessage("flutter/textinput", set_state);
904
905 // update editing state with deltas
906 g_autoptr(FlValue) deltas = build_list({
907 fl_value_new_int(1), // client_id
908 build_map({{
909 "deltas",
910 build_list({
912 .old_text = "Flutter",
913 .delta_text = "Flutter",
914 .delta_start = 7,
915 .delta_end = 7,
916 .selection_base = 0,
917 .selection_extent = 0,
918 }),
919 }),
920 }}),
921 });
922
923 EXPECT_CALL(messenger,
925 ::testing::Eq<FlBinaryMessenger*>(messenger),
926 ::testing::StrEq("flutter/textinput"),
927 MethodCall("TextInputClient.updateEditingStateWithDeltas",
928 FlValueEq(deltas)),
929 ::testing::_, ::testing::_, ::testing::_));
930
931 send_key_event(plugin, GDK_KEY_Home);
932}
933
934TEST(FlTextInputPluginTest, ComposingDelta) {
935 ::testing::NiceMock<flutter::testing::MockBinaryMessenger> messenger;
936 ::testing::NiceMock<flutter::testing::MockIMContext> context;
937 ::testing::NiceMock<flutter::testing::MockTextInputViewDelegate> delegate;
938
939 g_autoptr(FlTextInputPlugin) plugin =
940 fl_text_input_plugin_new(messenger, context, delegate);
941 EXPECT_NE(plugin, nullptr);
942
943 // set config
944 g_autoptr(FlValue) args = build_input_config({
945 .client_id = 1,
946 .enable_delta_model = true,
947 });
948 g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
950 FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr);
951
952 g_autoptr(FlValue) null = fl_value_new_null();
953 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
954 ::testing::Eq<FlBinaryMessenger*>(messenger),
955 ::testing::A<FlBinaryMessengerResponseHandle*>(),
956 SuccessResponse(null), ::testing::A<GError**>()))
957 .WillOnce(::testing::Return(true));
958
959 messenger.ReceiveMessage("flutter/textinput", set_client);
960
961 g_signal_emit_by_name(context, "preedit-start", nullptr);
962
963 // update
964 EXPECT_CALL(context,
965 gtk_im_context_get_preedit_string(
966 ::testing::Eq<GtkIMContext*>(context),
967 ::testing::A<gchar**>(), ::testing::_, ::testing::A<gint*>()))
968 .WillOnce(
969 ::testing::DoAll(::testing::SetArgPointee<1>(g_strdup("Flutter ")),
970 ::testing::SetArgPointee<3>(8)));
971
972 g_autoptr(FlValue) update = build_list({
973 fl_value_new_int(1), // client_id
974 build_map({{
975 "deltas",
976 build_list({
978 .old_text = "",
979 .delta_text = "Flutter ",
980 .delta_start = 0,
981 .delta_end = 0,
982 .selection_base = 8,
983 .selection_extent = 8,
984 .composing_base = 0,
985 .composing_extent = 8,
986 }),
987 }),
988 }}),
989 });
990
991 EXPECT_CALL(messenger,
993 ::testing::Eq<FlBinaryMessenger*>(messenger),
994 ::testing::StrEq("flutter/textinput"),
995 MethodCall("TextInputClient.updateEditingStateWithDeltas",
996 FlValueEq(update)),
997 ::testing::_, ::testing::_, ::testing::_));
998
999 g_signal_emit_by_name(context, "preedit-changed", nullptr);
1000
1001 // commit
1002 g_autoptr(FlValue) commit = build_list({
1003 fl_value_new_int(1), // client_id
1004 build_map({{
1005 "deltas",
1006 build_list({
1008 .old_text = "Flutter ",
1009 .delta_text = "Flutter engine",
1010 .delta_start = 0,
1011 .delta_end = 8,
1012 .selection_base = 14,
1013 .selection_extent = 14,
1014 .composing_base = -1,
1015 .composing_extent = -1,
1016 }),
1017 }),
1018 }}),
1019 });
1020
1021 EXPECT_CALL(messenger,
1023 ::testing::Eq<FlBinaryMessenger*>(messenger),
1024 ::testing::StrEq("flutter/textinput"),
1025 MethodCall("TextInputClient.updateEditingStateWithDeltas",
1026 FlValueEq(commit)),
1027 ::testing::_, ::testing::_, ::testing::_));
1028
1029 g_signal_emit_by_name(context, "commit", "Flutter engine", nullptr);
1030
1031 // end
1032 g_autoptr(FlValue) end = build_list({
1033 fl_value_new_int(1), // client_id
1034 build_map({{
1035 "deltas",
1036 build_list({
1038 .old_text = "Flutter engine",
1039 .selection_base = 14,
1040 .selection_extent = 14,
1041 }),
1042 }),
1043 }}),
1044 });
1045
1046 EXPECT_CALL(messenger,
1048 ::testing::Eq<FlBinaryMessenger*>(messenger),
1049 ::testing::StrEq("flutter/textinput"),
1050 MethodCall("TextInputClient.updateEditingStateWithDeltas",
1051 FlValueEq(end)),
1052 ::testing::_, ::testing::_, ::testing::_));
1053
1054 g_signal_emit_by_name(context, "preedit-end", nullptr);
1055}
1056
1057TEST(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();
1073 FL_METHOD_CODEC(codec), "TextInput.setClient", args, nullptr);
1074
1075 g_autoptr(FlValue) null = fl_value_new_null();
1076 EXPECT_CALL(messenger, fl_binary_messenger_send_response(
1077 ::testing::Eq<FlBinaryMessenger*>(messenger),
1078 ::testing::A<FlBinaryMessengerResponseHandle*>(),
1079 SuccessResponse(null), ::testing::A<GError**>()))
1080 .WillOnce(::testing::Return(true));
1081
1082 messenger.ReceiveMessage("flutter/textinput", set_client);
1083
1084 // commit F
1085 g_autoptr(FlValue) commit = build_list({
1086 fl_value_new_int(1), // client_id
1087 build_map({{
1088 "deltas",
1089 build_list({
1091 .old_text = "",
1092 .delta_text = "F",
1093 .delta_start = 0,
1094 .delta_end = 0,
1095 .selection_base = 1,
1096 .selection_extent = 1,
1097 .composing_base = -1,
1098 .composing_extent = -1,
1099 }),
1100 }),
1101 }}),
1102 });
1103
1104 EXPECT_CALL(messenger,
1106 ::testing::Eq<FlBinaryMessenger*>(messenger),
1107 ::testing::StrEq("flutter/textinput"),
1108 MethodCall("TextInputClient.updateEditingStateWithDeltas",
1109 FlValueEq(commit)),
1110 ::testing::_, ::testing::_, ::testing::_));
1111
1112 g_signal_emit_by_name(context, "commit", "F", nullptr);
1113
1114 // commit l
1115 g_autoptr(FlValue) commitL = build_list({
1116 fl_value_new_int(1), // client_id
1117 build_map({{
1118 "deltas",
1119 build_list({
1121 .old_text = "F",
1122 .delta_text = "l",
1123 .delta_start = 1,
1124 .delta_end = 1,
1125 .selection_base = 2,
1126 .selection_extent = 2,
1127 .composing_base = -1,
1128 .composing_extent = -1,
1129 }),
1130 }),
1131 }}),
1132 });
1133
1134 EXPECT_CALL(messenger,
1136 ::testing::Eq<FlBinaryMessenger*>(messenger),
1137 ::testing::StrEq("flutter/textinput"),
1138 MethodCall("TextInputClient.updateEditingStateWithDeltas",
1139 FlValueEq(commitL)),
1140 ::testing::_, ::testing::_, ::testing::_));
1141
1142 g_signal_emit_by_name(context, "commit", "l", nullptr);
1143
1144 // commit u
1145 g_autoptr(FlValue) commitU = build_list({
1146 fl_value_new_int(1), // client_id
1147 build_map({{
1148 "deltas",
1149 build_list({
1151 .old_text = "Fl",
1152 .delta_text = "u",
1153 .delta_start = 2,
1154 .delta_end = 2,
1155 .selection_base = 3,
1156 .selection_extent = 3,
1157 .composing_base = -1,
1158 .composing_extent = -1,
1159 }),
1160 }),
1161 }}),
1162 });
1163
1164 EXPECT_CALL(messenger,
1166 ::testing::Eq<FlBinaryMessenger*>(messenger),
1167 ::testing::StrEq("flutter/textinput"),
1168 MethodCall("TextInputClient.updateEditingStateWithDeltas",
1169 FlValueEq(commitU)),
1170 ::testing::_, ::testing::_, ::testing::_));
1171
1172 g_signal_emit_by_name(context, "commit", "u", nullptr);
1173
1174 // commit t
1175 g_autoptr(FlValue) commitTa = build_list({
1176 fl_value_new_int(1), // client_id
1177 build_map({{
1178 "deltas",
1179 build_list({
1181 .old_text = "Flu",
1182 .delta_text = "t",
1183 .delta_start = 3,
1184 .delta_end = 3,
1185 .selection_base = 4,
1186 .selection_extent = 4,
1187 .composing_base = -1,
1188 .composing_extent = -1,
1189 }),
1190 }),
1191 }}),
1192 });
1193
1194 EXPECT_CALL(messenger,
1196 ::testing::Eq<FlBinaryMessenger*>(messenger),
1197 ::testing::StrEq("flutter/textinput"),
1198 MethodCall("TextInputClient.updateEditingStateWithDeltas",
1199 FlValueEq(commitTa)),
1200 ::testing::_, ::testing::_, ::testing::_));
1201
1202 g_signal_emit_by_name(context, "commit", "t", nullptr);
1203
1204 // commit t again
1205 g_autoptr(FlValue) commitTb = build_list({
1206 fl_value_new_int(1), // client_id
1207 build_map({{
1208 "deltas",
1209 build_list({
1211 .old_text = "Flut",
1212 .delta_text = "t",
1213 .delta_start = 4,
1214 .delta_end = 4,
1215 .selection_base = 5,
1216 .selection_extent = 5,
1217 .composing_base = -1,
1218 .composing_extent = -1,
1219 }),
1220 }),
1221 }}),
1222 });
1223
1224 EXPECT_CALL(messenger,
1226 ::testing::Eq<FlBinaryMessenger*>(messenger),
1227 ::testing::StrEq("flutter/textinput"),
1228 MethodCall("TextInputClient.updateEditingStateWithDeltas",
1229 FlValueEq(commitTb)),
1230 ::testing::_, ::testing::_, ::testing::_));
1231
1232 g_signal_emit_by_name(context, "commit", "t", nullptr);
1233
1234 // commit e
1235 g_autoptr(FlValue) commitE = build_list({
1236 fl_value_new_int(1), // client_id
1237 build_map({{
1238 "deltas",
1239 build_list({
1241 .old_text = "Flutt",
1242 .delta_text = "e",
1243 .delta_start = 5,
1244 .delta_end = 5,
1245 .selection_base = 6,
1246 .selection_extent = 6,
1247 .composing_base = -1,
1248 .composing_extent = -1,
1249 }),
1250 }),
1251 }}),
1252 });
1253
1254 EXPECT_CALL(messenger,
1256 ::testing::Eq<FlBinaryMessenger*>(messenger),
1257 ::testing::StrEq("flutter/textinput"),
1258 MethodCall("TextInputClient.updateEditingStateWithDeltas",
1259 FlValueEq(commitE)),
1260 ::testing::_, ::testing::_, ::testing::_));
1261
1262 g_signal_emit_by_name(context, "commit", "e", nullptr);
1263
1264 // commit r
1265 g_autoptr(FlValue) commitR = build_list({
1266 fl_value_new_int(1), // client_id
1267 build_map({{
1268 "deltas",
1269 build_list({
1271 .old_text = "Flutte",
1272 .delta_text = "r",
1273 .delta_start = 6,
1274 .delta_end = 6,
1275 .selection_base = 7,
1276 .selection_extent = 7,
1277 .composing_base = -1,
1278 .composing_extent = -1,
1279 }),
1280 }),
1281 }}),
1282 });
1283
1284 EXPECT_CALL(messenger,
1286 ::testing::Eq<FlBinaryMessenger*>(messenger),
1287 ::testing::StrEq("flutter/textinput"),
1288 MethodCall("TextInputClient.updateEditingStateWithDeltas",
1289 FlValueEq(commitR)),
1290 ::testing::_, ::testing::_, ::testing::_));
1291
1292 g_signal_emit_by_name(context, "commit", "r", nullptr);
1293}
#define TEST(S, s, D, expected)
bool MatchAndExplain(GBytes *method_call, ::testing::MatchResultListener *result_listener) const
MethodCallMatcher(::testing::Matcher< std::string > name, ::testing::Matcher< FlValue * > args)
void DescribeTo(std::ostream *os) const
void DescribeNegationTo(std::ostream *os) const
AtkStateType state
glong glong end
G_MODULE_EXPORT void fl_binary_messenger_send_on_channel(FlBinaryMessenger *self, const gchar *channel, GBytes *message, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
G_MODULE_EXPORT gboolean fl_binary_messenger_send_response(FlBinaryMessenger *self, FlBinaryMessengerResponseHandle *response_handle, GBytes *response, GError **error)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
G_MODULE_EXPORT FlJsonMethodCodec * fl_json_method_codec_new()
void fl_key_event_dispose(FlKeyEvent *event)
FlKeyEvent * fl_key_event_new_from_gdk_event(GdkEvent *event)
G_BEGIN_DECLS G_MODULE_EXPORT FlMethodCall * method_call
FlMethodResponse * fl_method_codec_decode_response(FlMethodCodec *self, GBytes *message, GError **error)
gboolean fl_method_codec_decode_method_call(FlMethodCodec *self, GBytes *message, gchar **name, FlValue **args, GError **error)
GBytes * fl_method_codec_encode_method_call(FlMethodCodec *self, const gchar *name, FlValue *args, GError **error)
G_MODULE_EXPORT FlValue * fl_method_response_get_result(FlMethodResponse *self, GError **error)
const uint8_t uint32_t uint32_t GError ** error
uint8_t value
GAsyncResult * result
static FlMethodResponse * set_client(FlTextInputPlugin *self, FlValue *args)
static FlMethodResponse * show(FlTextInputPlugin *self)
static FlMethodResponse * set_marked_text_rect(FlTextInputPlugin *self, FlValue *args)
static FlMethodResponse * set_editable_size_and_transform(FlTextInputPlugin *self, FlValue *args)
gboolean fl_text_input_plugin_filter_keypress(FlTextInputPlugin *self, FlKeyEvent *event)
FlTextInputPlugin * fl_text_input_plugin_new(FlBinaryMessenger *messenger, GtkIMContext *im_context, FlTextInputViewDelegate *view_delegate)
MATCHER_P(SuccessResponse, result, "")
static FlValue * build_input_config(InputConfig config)
static FlValue * build_editing_state(EditingState state)
void printTo(FlMethodResponse *response, ::std::ostream *os)
static FlValue * build_map(std::map< const gchar *, FlValue * > args)
::testing::Matcher< GBytes * > MethodCall(const std::string &name, ::testing::Matcher< FlValue * > args)
static void send_key_event(FlTextInputPlugin *plugin, gint keyval, gint state=0)
static FlValue * build_list(std::vector< FlValue * > args)
static FlValue * build_editing_delta(EditingDelta delta)
void fl_text_input_view_delegate_translate_coordinates(FlTextInputViewDelegate *self, gint view_x, gint view_y, gint *window_x, gint *window_y)
G_MODULE_EXPORT FlValue * fl_value_new_map()
Definition fl_value.cc:366
G_MODULE_EXPORT void fl_value_set_string_take(FlValue *self, const gchar *key, FlValue *value)
Definition fl_value.cc:650
G_MODULE_EXPORT FlValue * fl_value_new_null()
Definition fl_value.cc:251
G_MODULE_EXPORT FlValue * fl_value_new_string(const gchar *value)
Definition fl_value.cc:276
G_MODULE_EXPORT FlValue * fl_value_new_bool(bool value)
Definition fl_value.cc:255
G_MODULE_EXPORT FlValue * fl_value_new_int(int64_t value)
Definition fl_value.cc:262
G_MODULE_EXPORT FlValue * fl_value_new_float(double value)
Definition fl_value.cc:269
G_MODULE_EXPORT void fl_value_append_take(FlValue *self, FlValue *value)
Definition fl_value.cc:600
G_MODULE_EXPORT FlValue * fl_value_new_list()
Definition fl_value.cc:349
G_MODULE_EXPORT bool fl_value_equal(FlValue *a, FlValue *b)
Definition fl_value.cc:471
typedefG_BEGIN_DECLS struct _FlValue FlValue
Definition fl_value.h:42
const char * name
Definition fuchsia.cc:50
FlutterKeyEvent key_event
Win32Message message
Definition ref_ptr.h:256
#define EXPECT_TRUE(handle)
Definition unit_test.h:685