1 // Copyright (c) 2012 The Chromium 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.
13 #include "base/i18n/char_iterator.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chromeos/dbus/dbus_thread_manager.h"
17 #include "chromeos/dbus/ibus/ibus_text.h"
18 #include "chromeos/dbus/ibus/mock_ibus_client.h"
19 #include "chromeos/ime/ibus_bridge.h"
20 #include "chromeos/ime/mock_ime_candidate_window_handler.h"
21 #include "chromeos/ime/mock_ime_engine_handler.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "ui/base/ime/input_method_delegate.h"
24 #include "ui/base/ime/input_method_ibus.h"
25 #include "ui/base/ime/text_input_client.h"
26 #include "ui/gfx/rect.h"
30 const uint32 kTestIBusKeyVal1 = 97;
31 const uint32 kTestIBusKeyVal2 = 30;
32 const uint32 kTestIBusKeyCode1 = 98;
33 const uint32 kTestIBusKeyCode2 = 48;
34 const uint32 kTestIBusState1 = 99;
35 const uint32 kTestIBusState2 = 46;
37 typedef chromeos::IBusEngineHandlerInterface::KeyEventDoneCallback
40 uint32 GetOffsetInUTF16(const std::string& utf8_string, uint32 utf8_offset) {
41 string16 utf16_string = UTF8ToUTF16(utf8_string);
42 DCHECK_LT(utf8_offset, utf16_string.size());
43 base::i18n::UTF16CharIterator char_iterator(&utf16_string);
44 for (size_t i = 0; i < utf8_offset; ++i)
45 char_iterator.Advance();
46 return char_iterator.array_pos();
49 bool IsEqualXKeyEvent(const XEvent& e1, const XEvent& e2) {
50 if ((e1.type == KeyPress && e2.type == KeyPress) ||
51 (e1.type == KeyRelease && e2.type == KeyRelease)) {
52 return !std::memcmp(&e1.xkey, &e2.xkey, sizeof(XKeyEvent));
57 enum KeyEventHandlerBehavior {
65 class TestableInputMethodIBus : public InputMethodIBus {
67 explicit TestableInputMethodIBus(internal::InputMethodDelegate* delegate)
68 : InputMethodIBus(delegate),
69 process_key_event_post_ime_call_count_(0) {
72 struct ProcessKeyEventPostIMEArgs {
73 ProcessKeyEventPostIMEArgs() : handled(false) {
74 std::memset(&event, 0, sizeof(XEvent));
80 struct IBusKeyEventFromNativeKeyEventResult {
81 IBusKeyEventFromNativeKeyEventResult() : keyval(0), keycode(0), state(0) {}
87 // InputMethodIBus override.
88 virtual void ProcessKeyEventPostIME(const base::NativeEvent& native_key_event,
90 bool handled) OVERRIDE {
91 process_key_event_post_ime_args_.event = *native_key_event;
92 process_key_event_post_ime_args_.handled = handled;
93 ++process_key_event_post_ime_call_count_;
96 // We can't call X11 related function without display in unit test, so
97 // override with mock function.
98 virtual void IBusKeyEventFromNativeKeyEvent(
99 const base::NativeEvent& native_event,
101 uint32* ibus_keycode,
102 uint32* ibus_state) OVERRIDE {
103 EXPECT_TRUE(native_event);
104 EXPECT_TRUE(ibus_keyval);
105 EXPECT_TRUE(ibus_keycode);
106 EXPECT_TRUE(ibus_state);
107 *ibus_keyval = ibus_key_event_from_native_key_event_result_.keyval;
108 *ibus_keycode = ibus_key_event_from_native_key_event_result_.keycode;
109 *ibus_state = ibus_key_event_from_native_key_event_result_.state;
112 void ResetCallCount() {
113 process_key_event_post_ime_call_count_ = 0;
116 const ProcessKeyEventPostIMEArgs& process_key_event_post_ime_args() const {
117 return process_key_event_post_ime_args_;
120 int process_key_event_post_ime_call_count() const {
121 return process_key_event_post_ime_call_count_;
124 IBusKeyEventFromNativeKeyEventResult*
125 mutable_ibus_key_event_from_native_key_event_result() {
126 return &ibus_key_event_from_native_key_event_result_;
129 // Change access rights for testing.
130 using InputMethodIBus::ExtractCompositionText;
131 using InputMethodIBus::ResetContext;
134 ProcessKeyEventPostIMEArgs process_key_event_post_ime_args_;
135 int process_key_event_post_ime_call_count_;
137 IBusKeyEventFromNativeKeyEventResult
138 ibus_key_event_from_native_key_event_result_;
141 class CreateInputContextSuccessHandler {
143 explicit CreateInputContextSuccessHandler(const dbus::ObjectPath& object_path)
144 : object_path_(object_path) {
147 void Run(const std::string& client_name,
148 const chromeos::IBusClient::CreateInputContextCallback& callback) {
149 EXPECT_EQ("chrome", client_name);
150 callback.Run(object_path_);
154 dbus::ObjectPath object_path_;
156 DISALLOW_COPY_AND_ASSIGN(CreateInputContextSuccessHandler);
159 class CreateInputContextNoResponseHandler {
161 CreateInputContextNoResponseHandler() {}
162 void Run(const std::string& client_name,
163 const chromeos::IBusClient::CreateInputContextCallback& callback) {
167 DISALLOW_COPY_AND_ASSIGN(CreateInputContextNoResponseHandler);
170 class CreateInputContextDelayHandler {
172 explicit CreateInputContextDelayHandler(const dbus::ObjectPath& object_path)
173 : object_path_(object_path) {
176 void Run(const std::string& client_name,
177 const chromeos::IBusClient::CreateInputContextCallback& callback) {
178 callback_ = callback;
182 callback_.Run(object_path_);
186 dbus::ObjectPath object_path_;
187 chromeos::IBusClient::CreateInputContextCallback callback_;
189 DISALLOW_COPY_AND_ASSIGN(CreateInputContextDelayHandler);
192 class SynchronousKeyEventHandler {
194 SynchronousKeyEventHandler(uint32 expected_keyval,
195 uint32 expected_keycode,
196 uint32 expected_state,
197 KeyEventHandlerBehavior behavior)
198 : expected_keyval_(expected_keyval),
199 expected_keycode_(expected_keycode),
200 expected_state_(expected_state),
201 behavior_(behavior) {}
203 virtual ~SynchronousKeyEventHandler() {}
205 void Run(uint32 keyval,
208 const KeyEventCallback& callback) {
209 EXPECT_EQ(expected_keyval_, keyval);
210 EXPECT_EQ(expected_keycode_, keycode);
211 EXPECT_EQ(expected_state_, state);
212 callback.Run(behavior_ == KEYEVENT_CONSUME);
216 const uint32 expected_keyval_;
217 const uint32 expected_keycode_;
218 const uint32 expected_state_;
219 const KeyEventHandlerBehavior behavior_;
221 DISALLOW_COPY_AND_ASSIGN(SynchronousKeyEventHandler);
224 class AsynchronousKeyEventHandler {
226 AsynchronousKeyEventHandler(uint32 expected_keyval,
227 uint32 expected_keycode,
228 uint32 expected_state)
229 : expected_keyval_(expected_keyval),
230 expected_keycode_(expected_keycode),
231 expected_state_(expected_state) {}
233 virtual ~AsynchronousKeyEventHandler() {}
235 void Run(uint32 keyval,
238 const KeyEventCallback& callback) {
239 EXPECT_EQ(expected_keyval_, keyval);
240 EXPECT_EQ(expected_keycode_, keycode);
241 EXPECT_EQ(expected_state_, state);
242 callback_ = callback;
245 void RunCallback(KeyEventHandlerBehavior behavior) {
246 callback_.Run(behavior == KEYEVENT_CONSUME);
250 const uint32 expected_keyval_;
251 const uint32 expected_keycode_;
252 const uint32 expected_state_;
253 KeyEventCallback callback_;
255 DISALLOW_COPY_AND_ASSIGN(AsynchronousKeyEventHandler);
258 class SetSurroundingTextVerifier {
260 SetSurroundingTextVerifier(const std::string& expected_surrounding_text,
261 uint32 expected_cursor_position,
262 uint32 expected_anchor_position)
263 : expected_surrounding_text_(expected_surrounding_text),
264 expected_cursor_position_(expected_cursor_position),
265 expected_anchor_position_(expected_anchor_position) {}
267 void Verify(const std::string& text,
270 EXPECT_EQ(expected_surrounding_text_, text);
271 EXPECT_EQ(expected_cursor_position_, cursor_pos);
272 EXPECT_EQ(expected_anchor_position_, anchor_pos);
276 const std::string expected_surrounding_text_;
277 const uint32 expected_cursor_position_;
278 const uint32 expected_anchor_position_;
280 DISALLOW_COPY_AND_ASSIGN(SetSurroundingTextVerifier);
283 class InputMethodIBusTest : public internal::InputMethodDelegate,
284 public testing::Test,
285 public TextInputClient {
287 InputMethodIBusTest() {
291 virtual ~InputMethodIBusTest() {
294 // testing::Test overrides:
295 virtual void SetUp() OVERRIDE {
296 chromeos::IBusBridge::Initialize();
298 mock_ime_engine_handler_.reset(
299 new chromeos::MockIMEEngineHandler());
300 chromeos::IBusBridge::Get()->SetEngineHandler(
301 mock_ime_engine_handler_.get());
303 mock_ime_candidate_window_handler_.reset(
304 new chromeos::MockIMECandidateWindowHandler());
305 chromeos::IBusBridge::Get()->SetCandidateWindowHandler(
306 mock_ime_candidate_window_handler_.get());
308 ime_.reset(new TestableInputMethodIBus(this));
309 ime_->SetFocusedTextInputClient(this);
312 virtual void TearDown() OVERRIDE {
314 ime_->SetFocusedTextInputClient(NULL);
316 chromeos::IBusBridge::Get()->SetEngineHandler(NULL);
317 chromeos::IBusBridge::Get()->SetCandidateWindowHandler(NULL);
318 mock_ime_engine_handler_.reset();
319 mock_ime_candidate_window_handler_.reset();
320 chromeos::IBusBridge::Shutdown();
323 // ui::internal::InputMethodDelegate overrides:
324 virtual bool DispatchKeyEventPostIME(
325 const base::NativeEvent& native_key_event) OVERRIDE {
326 dispatched_native_event_ = native_key_event;
329 virtual bool DispatchFabricatedKeyEventPostIME(ui::EventType type,
330 ui::KeyboardCode key_code,
331 int flags) OVERRIDE {
332 dispatched_fabricated_event_type_ = type;
333 dispatched_fabricated_event_key_code_ = key_code;
334 dispatched_fabricated_event_flags_ = flags;
338 // ui::TextInputClient overrides:
339 virtual void SetCompositionText(
340 const CompositionText& composition) OVERRIDE {
341 composition_text_ = composition;
343 virtual void ConfirmCompositionText() OVERRIDE {
344 confirmed_text_ = composition_text_;
345 composition_text_.Clear();
347 virtual void ClearCompositionText() OVERRIDE {
348 composition_text_.Clear();
350 virtual void InsertText(const string16& text) OVERRIDE {
351 inserted_text_ = text;
353 virtual void InsertChar(char16 ch, int flags) OVERRIDE {
355 inserted_char_flags_ = flags;
357 virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE {
358 return static_cast<gfx::NativeWindow>(NULL);
360 virtual TextInputType GetTextInputType() const OVERRIDE {
363 virtual TextInputMode GetTextInputMode() const OVERRIDE {
364 return TEXT_INPUT_MODE_DEFAULT;
366 virtual bool CanComposeInline() const OVERRIDE {
367 return can_compose_inline_;
369 virtual gfx::Rect GetCaretBounds() const OVERRIDE {
370 return caret_bounds_;
372 virtual bool GetCompositionCharacterBounds(uint32 index,
373 gfx::Rect* rect) const OVERRIDE {
376 virtual bool HasCompositionText() const OVERRIDE {
377 CompositionText empty;
378 return composition_text_ != empty;
380 virtual bool GetTextRange(gfx::Range* range) const OVERRIDE {
381 *range = text_range_;
384 virtual bool GetCompositionTextRange(gfx::Range* range) const OVERRIDE {
387 virtual bool GetSelectionRange(gfx::Range* range) const OVERRIDE {
388 *range = selection_range_;
392 virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE { return false; }
393 virtual bool DeleteRange(const gfx::Range& range) OVERRIDE { return false; }
394 virtual bool GetTextFromRange(const gfx::Range& range,
395 string16* text) const OVERRIDE {
396 *text = surrounding_text_.substr(range.GetMin(), range.length());
399 virtual void OnInputMethodChanged() OVERRIDE {
400 ++on_input_method_changed_call_count_;
402 virtual bool ChangeTextDirectionAndLayoutAlignment(
403 base::i18n::TextDirection direction) OVERRIDE { return false; }
404 virtual void ExtendSelectionAndDelete(size_t before,
405 size_t after) OVERRIDE { }
406 virtual void EnsureCaretInRect(const gfx::Rect& rect) OVERRIDE { }
408 bool HasNativeEvent() const {
409 base::NativeEvent empty;
410 std::memset(&empty, 0, sizeof(empty));
411 return !!std::memcmp(&dispatched_native_event_,
413 sizeof(dispatched_native_event_));
417 std::memset(&dispatched_native_event_, 0, sizeof(dispatched_native_event_));
418 DCHECK(!HasNativeEvent());
419 dispatched_fabricated_event_type_ = ET_UNKNOWN;
420 dispatched_fabricated_event_key_code_ = VKEY_UNKNOWN;
421 dispatched_fabricated_event_flags_ = 0;
423 composition_text_.Clear();
424 confirmed_text_.Clear();
425 inserted_text_.clear();
427 inserted_char_flags_ = 0;
428 on_input_method_changed_call_count_ = 0;
430 input_type_ = TEXT_INPUT_TYPE_NONE;
431 can_compose_inline_ = true;
432 caret_bounds_ = gfx::Rect();
435 scoped_ptr<TestableInputMethodIBus> ime_;
437 // Variables for remembering the parameters that are passed to
438 // ui::internal::InputMethodDelegate functions.
439 base::NativeEvent dispatched_native_event_;
440 ui::EventType dispatched_fabricated_event_type_;
441 ui::KeyboardCode dispatched_fabricated_event_key_code_;
442 int dispatched_fabricated_event_flags_;
444 // Variables for remembering the parameters that are passed to
445 // ui::TextInputClient functions.
446 CompositionText composition_text_;
447 CompositionText confirmed_text_;
448 string16 inserted_text_;
449 char16 inserted_char_;
450 unsigned int on_input_method_changed_call_count_;
451 int inserted_char_flags_;
453 // Variables that will be returned from the ui::TextInputClient functions.
454 TextInputType input_type_;
455 bool can_compose_inline_;
456 gfx::Rect caret_bounds_;
457 gfx::Range text_range_;
458 gfx::Range selection_range_;
459 string16 surrounding_text_;
461 scoped_ptr<chromeos::MockIMEEngineHandler> mock_ime_engine_handler_;
462 scoped_ptr<chromeos::MockIMECandidateWindowHandler>
463 mock_ime_candidate_window_handler_;
465 DISALLOW_COPY_AND_ASSIGN(InputMethodIBusTest);
468 // Tests public APIs in ui::InputMethod first.
470 TEST_F(InputMethodIBusTest, GetInputLocale) {
471 // ui::InputMethodIBus does not support the API.
473 EXPECT_EQ("", ime_->GetInputLocale());
476 TEST_F(InputMethodIBusTest, GetInputTextDirection) {
477 // ui::InputMethodIBus does not support the API.
479 EXPECT_EQ(base::i18n::UNKNOWN_DIRECTION, ime_->GetInputTextDirection());
482 TEST_F(InputMethodIBusTest, IsActive) {
484 // ui::InputMethodIBus always returns true.
485 EXPECT_TRUE(ime_->IsActive());
488 TEST_F(InputMethodIBusTest, GetInputTextType) {
490 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
491 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
492 ime_->OnTextInputTypeChanged(this);
493 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
494 input_type_ = TEXT_INPUT_TYPE_TEXT;
495 ime_->OnTextInputTypeChanged(this);
496 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT, ime_->GetTextInputType());
499 TEST_F(InputMethodIBusTest, CanComposeInline) {
501 EXPECT_TRUE(ime_->CanComposeInline());
502 can_compose_inline_ = false;
503 ime_->OnTextInputTypeChanged(this);
504 EXPECT_FALSE(ime_->CanComposeInline());
507 TEST_F(InputMethodIBusTest, GetTextInputClient) {
509 EXPECT_EQ(this, ime_->GetTextInputClient());
510 ime_->SetFocusedTextInputClient(NULL);
511 EXPECT_EQ(NULL, ime_->GetTextInputClient());
514 TEST_F(InputMethodIBusTest, GetInputTextType_WithoutFocusedClient) {
516 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
517 ime_->SetFocusedTextInputClient(NULL);
518 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
519 ime_->OnTextInputTypeChanged(this);
520 // The OnTextInputTypeChanged() call above should be ignored since |this| is
521 // not the current focused client.
522 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
524 ime_->SetFocusedTextInputClient(this);
525 ime_->OnTextInputTypeChanged(this);
526 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
529 TEST_F(InputMethodIBusTest, GetInputTextType_WithoutFocusedWindow) {
531 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
533 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
534 ime_->OnTextInputTypeChanged(this);
535 // The OnTextInputTypeChanged() call above should be ignored since the top-
536 // level window which the ime_ is attached to is not currently focused.
537 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
540 ime_->OnTextInputTypeChanged(this);
541 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
544 TEST_F(InputMethodIBusTest, GetInputTextType_WithoutFocusedWindow2) {
545 ime_->Init(false); // the top-level is initially unfocused.
546 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
547 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
548 ime_->OnTextInputTypeChanged(this);
549 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
552 ime_->OnTextInputTypeChanged(this);
553 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
556 // Confirm that IBusClient::FocusIn is called on "connected" if input_type_ is
558 TEST_F(InputMethodIBusTest, FocusIn_Text) {
560 // A context shouldn't be created since the daemon is not running.
561 EXPECT_EQ(0U, on_input_method_changed_call_count_);
562 // Click a text input form.
563 input_type_ = TEXT_INPUT_TYPE_TEXT;
564 ime_->OnTextInputTypeChanged(this);
565 // Since a form has focus, IBusClient::FocusIn() should be called.
566 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
569 mock_ime_candidate_window_handler_->set_cursor_location_call_count());
570 // ui::TextInputClient::OnInputMethodChanged() should be called when
571 // ui::InputMethodIBus connects/disconnects to/from ibus-daemon and the
572 // current text input type is not NONE.
573 EXPECT_EQ(1U, on_input_method_changed_call_count_);
576 // Confirm that IBusClient::FocusIn is NOT called on "connected" if input_type_
578 TEST_F(InputMethodIBusTest, FocusIn_Password) {
580 EXPECT_EQ(0U, on_input_method_changed_call_count_);
581 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
582 ime_->OnTextInputTypeChanged(this);
583 // Since a form has focus, IBusClient::FocusIn() should NOT be called.
584 EXPECT_EQ(0, mock_ime_engine_handler_->focus_in_call_count());
585 EXPECT_EQ(1U, on_input_method_changed_call_count_);
588 // Confirm that IBusClient::FocusOut is called as expected.
589 TEST_F(InputMethodIBusTest, FocusOut_None) {
590 input_type_ = TEXT_INPUT_TYPE_TEXT;
592 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
593 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
594 input_type_ = TEXT_INPUT_TYPE_NONE;
595 ime_->OnTextInputTypeChanged(this);
596 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
597 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
600 // Confirm that IBusClient::FocusOut is called as expected.
601 TEST_F(InputMethodIBusTest, FocusOut_Password) {
602 input_type_ = TEXT_INPUT_TYPE_TEXT;
604 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
605 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
606 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
607 ime_->OnTextInputTypeChanged(this);
608 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
609 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
612 // FocusIn/FocusOut scenario test
613 TEST_F(InputMethodIBusTest, Focus_Scenario) {
615 // Confirm that both FocusIn and FocusOut are NOT called.
616 EXPECT_EQ(0, mock_ime_engine_handler_->focus_in_call_count());
617 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
618 EXPECT_EQ(chromeos::ibus::TEXT_INPUT_TYPE_NONE,
619 mock_ime_engine_handler_->last_text_input_type());
621 input_type_ = TEXT_INPUT_TYPE_TEXT;
622 ime_->OnTextInputTypeChanged(this);
623 // Confirm that only FocusIn is called and the TextInputType is TEXT.
624 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
625 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
626 EXPECT_EQ(chromeos::ibus::TEXT_INPUT_TYPE_TEXT,
627 mock_ime_engine_handler_->last_text_input_type());
629 ime_->OnTextInputTypeChanged(this);
630 // Confirm that both FocusIn and FocusOut are NOT called.
631 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
632 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
633 EXPECT_EQ(chromeos::ibus::TEXT_INPUT_TYPE_TEXT,
634 mock_ime_engine_handler_->last_text_input_type());
636 input_type_ = TEXT_INPUT_TYPE_URL;
637 ime_->OnTextInputTypeChanged(this);
638 // Confirm that both FocusIn and FocusOut are called and the TextInputType is
640 EXPECT_EQ(2, mock_ime_engine_handler_->focus_in_call_count());
641 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
642 EXPECT_EQ(chromeos::ibus::TEXT_INPUT_TYPE_URL,
643 mock_ime_engine_handler_->last_text_input_type());
646 // Test if the new |caret_bounds_| is correctly sent to ibus-daemon.
647 TEST_F(InputMethodIBusTest, OnCaretBoundsChanged) {
648 input_type_ = TEXT_INPUT_TYPE_TEXT;
652 mock_ime_candidate_window_handler_->set_cursor_location_call_count());
653 caret_bounds_ = gfx::Rect(1, 2, 3, 4);
654 ime_->OnCaretBoundsChanged(this);
657 mock_ime_candidate_window_handler_->set_cursor_location_call_count());
658 caret_bounds_ = gfx::Rect(0, 2, 3, 4);
659 ime_->OnCaretBoundsChanged(this);
662 mock_ime_candidate_window_handler_->set_cursor_location_call_count());
663 caret_bounds_ = gfx::Rect(0, 2, 3, 4); // unchanged
664 ime_->OnCaretBoundsChanged(this);
665 // Current InputMethodIBus implementation performs the IPC regardless of the
666 // bounds are changed or not.
669 mock_ime_candidate_window_handler_->set_cursor_location_call_count());
672 TEST_F(InputMethodIBusTest, ExtractCompositionTextTest_NoAttribute) {
673 const char kSampleText[] = "Sample Text";
674 const uint32 kCursorPos = 2UL;
676 const string16 utf16_string = UTF8ToUTF16(kSampleText);
677 chromeos::IBusText ibus_text;
678 ibus_text.set_text(kSampleText);
680 CompositionText composition_text;
681 ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
682 EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
683 // If there is no selection, |selection| represents cursor position.
684 EXPECT_EQ(kCursorPos, composition_text.selection.start());
685 EXPECT_EQ(kCursorPos, composition_text.selection.end());
686 // If there is no underline, |underlines| contains one underline and it is
687 // whole text underline.
688 ASSERT_EQ(1UL, composition_text.underlines.size());
689 EXPECT_EQ(0UL, composition_text.underlines[0].start_offset);
690 EXPECT_EQ(utf16_string.size(), composition_text.underlines[0].end_offset);
691 EXPECT_FALSE(composition_text.underlines[0].thick);
694 TEST_F(InputMethodIBusTest, ExtractCompositionTextTest_SingleUnderline) {
695 const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
696 "\xE3\x81\x88\xE3\x81\x8A";
697 const uint32 kCursorPos = 2UL;
699 // Set up ibus text with one underline attribute.
700 chromeos::IBusText ibus_text;
701 ibus_text.set_text(kSampleText);
702 chromeos::IBusText::UnderlineAttribute underline;
703 underline.type = chromeos::IBusText::IBUS_TEXT_UNDERLINE_SINGLE;
704 underline.start_index = 1UL;
705 underline.end_index = 4UL;
706 ibus_text.mutable_underline_attributes()->push_back(underline);
708 CompositionText composition_text;
709 ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
710 EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
711 // If there is no selection, |selection| represents cursor position.
712 EXPECT_EQ(kCursorPos, composition_text.selection.start());
713 EXPECT_EQ(kCursorPos, composition_text.selection.end());
714 ASSERT_EQ(1UL, composition_text.underlines.size());
715 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
716 composition_text.underlines[0].start_offset);
717 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
718 composition_text.underlines[0].end_offset);
719 // Single underline represents as black thin line.
720 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
721 EXPECT_FALSE(composition_text.underlines[0].thick);
724 TEST_F(InputMethodIBusTest, ExtractCompositionTextTest_DoubleUnderline) {
725 const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
726 "\xE3\x81\x88\xE3\x81\x8A";
727 const uint32 kCursorPos = 2UL;
729 // Set up ibus text with one underline attribute.
730 chromeos::IBusText ibus_text;
731 ibus_text.set_text(kSampleText);
732 chromeos::IBusText::UnderlineAttribute underline;
733 underline.type = chromeos::IBusText::IBUS_TEXT_UNDERLINE_DOUBLE;
734 underline.start_index = 1UL;
735 underline.end_index = 4UL;
736 ibus_text.mutable_underline_attributes()->push_back(underline);
738 CompositionText composition_text;
739 ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
740 EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
741 // If there is no selection, |selection| represents cursor position.
742 EXPECT_EQ(kCursorPos, composition_text.selection.start());
743 EXPECT_EQ(kCursorPos, composition_text.selection.end());
744 ASSERT_EQ(1UL, composition_text.underlines.size());
745 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
746 composition_text.underlines[0].start_offset);
747 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
748 composition_text.underlines[0].end_offset);
749 // Double underline represents as black thick line.
750 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
751 EXPECT_TRUE(composition_text.underlines[0].thick);
754 TEST_F(InputMethodIBusTest, ExtractCompositionTextTest_ErrorUnderline) {
755 const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
756 "\xE3\x81\x88\xE3\x81\x8A";
757 const uint32 kCursorPos = 2UL;
759 // Set up ibus text with one underline attribute.
760 chromeos::IBusText ibus_text;
761 ibus_text.set_text(kSampleText);
762 chromeos::IBusText::UnderlineAttribute underline;
763 underline.type = chromeos::IBusText::IBUS_TEXT_UNDERLINE_ERROR;
764 underline.start_index = 1UL;
765 underline.end_index = 4UL;
766 ibus_text.mutable_underline_attributes()->push_back(underline);
768 CompositionText composition_text;
769 ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
770 EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
771 EXPECT_EQ(kCursorPos, composition_text.selection.start());
772 EXPECT_EQ(kCursorPos, composition_text.selection.end());
773 ASSERT_EQ(1UL, composition_text.underlines.size());
774 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
775 composition_text.underlines[0].start_offset);
776 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
777 composition_text.underlines[0].end_offset);
778 // Error underline represents as red thin line.
779 EXPECT_EQ(SK_ColorRED, composition_text.underlines[0].color);
780 EXPECT_FALSE(composition_text.underlines[0].thick);
783 TEST_F(InputMethodIBusTest, ExtractCompositionTextTest_Selection) {
784 const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
785 "\xE3\x81\x88\xE3\x81\x8A";
786 const uint32 kCursorPos = 2UL;
788 // Set up ibus text with one underline attribute.
789 chromeos::IBusText ibus_text;
790 ibus_text.set_text(kSampleText);
791 chromeos::IBusText::SelectionAttribute selection;
792 selection.start_index = 1UL;
793 selection.end_index = 4UL;
794 ibus_text.mutable_selection_attributes()->push_back(selection);
796 CompositionText composition_text;
797 ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
798 EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
799 EXPECT_EQ(kCursorPos, composition_text.selection.start());
800 EXPECT_EQ(kCursorPos, composition_text.selection.end());
801 ASSERT_EQ(1UL, composition_text.underlines.size());
802 EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.start_index),
803 composition_text.underlines[0].start_offset);
804 EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.end_index),
805 composition_text.underlines[0].end_offset);
806 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
807 EXPECT_TRUE(composition_text.underlines[0].thick);
810 TEST_F(InputMethodIBusTest,
811 ExtractCompositionTextTest_SelectionStartWithCursor) {
812 const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
813 "\xE3\x81\x88\xE3\x81\x8A";
814 const uint32 kCursorPos = 1UL;
816 // Set up ibus text with one underline attribute.
817 chromeos::IBusText ibus_text;
818 ibus_text.set_text(kSampleText);
819 chromeos::IBusText::SelectionAttribute selection;
820 selection.start_index = kCursorPos;
821 selection.end_index = 4UL;
822 ibus_text.mutable_selection_attributes()->push_back(selection);
824 CompositionText composition_text;
825 ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
826 EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
827 // If the cursor position is same as selection bounds, selection start
828 // position become opposit side of selection from cursor.
829 EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.end_index),
830 composition_text.selection.start());
831 EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
832 composition_text.selection.end());
833 ASSERT_EQ(1UL, composition_text.underlines.size());
834 EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.start_index),
835 composition_text.underlines[0].start_offset);
836 EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.end_index),
837 composition_text.underlines[0].end_offset);
838 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
839 EXPECT_TRUE(composition_text.underlines[0].thick);
842 TEST_F(InputMethodIBusTest, ExtractCompositionTextTest_SelectionEndWithCursor) {
843 const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
844 "\xE3\x81\x88\xE3\x81\x8A";
845 const uint32 kCursorPos = 4UL;
847 // Set up ibus text with one underline attribute.
848 chromeos::IBusText ibus_text;
849 ibus_text.set_text(kSampleText);
850 chromeos::IBusText::SelectionAttribute selection;
851 selection.start_index = 1UL;
852 selection.end_index = kCursorPos;
853 ibus_text.mutable_selection_attributes()->push_back(selection);
855 CompositionText composition_text;
856 ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
857 EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
858 // If the cursor position is same as selection bounds, selection start
859 // position become opposit side of selection from cursor.
860 EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.start_index),
861 composition_text.selection.start());
862 EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
863 composition_text.selection.end());
864 ASSERT_EQ(1UL, composition_text.underlines.size());
865 EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.start_index),
866 composition_text.underlines[0].start_offset);
867 EXPECT_EQ(GetOffsetInUTF16(kSampleText, selection.end_index),
868 composition_text.underlines[0].end_offset);
869 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
870 EXPECT_TRUE(composition_text.underlines[0].thick);
873 TEST_F(InputMethodIBusTest, SurroundingText_NoSelectionTest) {
875 // Click a text input form.
876 input_type_ = TEXT_INPUT_TYPE_TEXT;
877 ime_->OnTextInputTypeChanged(this);
879 // Set the TextInputClient behaviors.
880 surrounding_text_ = UTF8ToUTF16("abcdef");
881 text_range_ = gfx::Range(0, 6);
882 selection_range_ = gfx::Range(3, 3);
884 // Set the verifier for SetSurroundingText mock call.
885 SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 3, 3);
888 ime_->OnCaretBoundsChanged(this);
890 // Check the call count.
892 mock_ime_engine_handler_->set_surrounding_text_call_count());
893 EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
894 mock_ime_engine_handler_->last_set_surrounding_text());
896 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
898 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
901 TEST_F(InputMethodIBusTest, SurroundingText_SelectionTest) {
903 // Click a text input form.
904 input_type_ = TEXT_INPUT_TYPE_TEXT;
905 ime_->OnTextInputTypeChanged(this);
907 // Set the TextInputClient behaviors.
908 surrounding_text_ = UTF8ToUTF16("abcdef");
909 text_range_ = gfx::Range(0, 6);
910 selection_range_ = gfx::Range(2, 5);
912 // Set the verifier for SetSurroundingText mock call.
913 SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 2, 5);
915 ime_->OnCaretBoundsChanged(this);
917 // Check the call count.
919 mock_ime_engine_handler_->set_surrounding_text_call_count());
920 EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
921 mock_ime_engine_handler_->last_set_surrounding_text());
923 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
925 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
928 TEST_F(InputMethodIBusTest, SurroundingText_PartialText) {
930 // Click a text input form.
931 input_type_ = TEXT_INPUT_TYPE_TEXT;
932 ime_->OnTextInputTypeChanged(this);
934 // Set the TextInputClient behaviors.
935 surrounding_text_ = UTF8ToUTF16("abcdefghij");
936 text_range_ = gfx::Range(5, 10);
937 selection_range_ = gfx::Range(7, 9);
939 ime_->OnCaretBoundsChanged(this);
941 // Check the call count.
943 mock_ime_engine_handler_->set_surrounding_text_call_count());
944 // Set the verifier for SetSurroundingText mock call.
945 // Here (2, 4) is selection range in expected surrounding text coordinates.
947 mock_ime_engine_handler_->last_set_surrounding_text());
949 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
951 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
954 TEST_F(InputMethodIBusTest, SurroundingText_BecomeEmptyText) {
956 // Click a text input form.
957 input_type_ = TEXT_INPUT_TYPE_TEXT;
958 ime_->OnTextInputTypeChanged(this);
960 // Set the TextInputClient behaviors.
961 // If the surrounding text becomes empty, text_range become (0, 0) and
962 // selection range become invalid.
963 surrounding_text_ = UTF8ToUTF16("");
964 text_range_ = gfx::Range(0, 0);
965 selection_range_ = gfx::Range::InvalidRange();
967 ime_->OnCaretBoundsChanged(this);
969 // Check the call count.
971 mock_ime_engine_handler_->set_surrounding_text_call_count());
973 // Should not be called twice with same condition.
974 ime_->OnCaretBoundsChanged(this);
976 mock_ime_engine_handler_->set_surrounding_text_call_count());
979 class InputMethodIBusKeyEventTest : public InputMethodIBusTest {
981 InputMethodIBusKeyEventTest() {}
982 virtual ~InputMethodIBusKeyEventTest() {}
984 virtual void SetUp() OVERRIDE {
985 InputMethodIBusTest::SetUp();
989 DISALLOW_COPY_AND_ASSIGN(InputMethodIBusKeyEventTest);
992 TEST_F(InputMethodIBusKeyEventTest, KeyEventDelayResponseTest) {
994 event.xkey.type = KeyPress;
996 // Set up IBusKeyEventFromNativeKeyEvent result.
997 ime_->mutable_ibus_key_event_from_native_key_event_result()->keyval
999 ime_->mutable_ibus_key_event_from_native_key_event_result()->keycode
1000 = kTestIBusKeyCode1;
1001 ime_->mutable_ibus_key_event_from_native_key_event_result()->state
1005 input_type_ = TEXT_INPUT_TYPE_TEXT;
1006 ime_->OnTextInputTypeChanged(this);
1007 ime_->DispatchKeyEvent(&event);
1009 // Check before state.
1011 mock_ime_engine_handler_->process_key_event_call_count());
1012 EXPECT_EQ(kTestIBusKeyVal1,
1013 mock_ime_engine_handler_->last_processed_keysym());
1014 EXPECT_EQ(kTestIBusKeyCode1,
1015 mock_ime_engine_handler_->last_processed_keycode());
1016 EXPECT_EQ(kTestIBusState1,
1017 mock_ime_engine_handler_->last_processed_state());
1018 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1021 mock_ime_engine_handler_->last_passed_callback().Run(true);
1023 // Check the results
1024 EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
1025 EXPECT_TRUE(IsEqualXKeyEvent(event,
1026 ime_->process_key_event_post_ime_args().event));
1027 EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
1030 TEST_F(InputMethodIBusKeyEventTest, MultiKeyEventDelayResponseTest) {
1032 input_type_ = TEXT_INPUT_TYPE_TEXT;
1033 ime_->OnTextInputTypeChanged(this);
1036 event.xkey.type = KeyPress;
1038 // Set up IBusKeyEventFromNativeKeyEvent result for first key event.
1039 ime_->mutable_ibus_key_event_from_native_key_event_result()->keyval
1041 ime_->mutable_ibus_key_event_from_native_key_event_result()->keycode
1042 = kTestIBusKeyCode1;
1043 ime_->mutable_ibus_key_event_from_native_key_event_result()->state
1047 ime_->DispatchKeyEvent(&event);
1048 EXPECT_EQ(kTestIBusKeyVal1,
1049 mock_ime_engine_handler_->last_processed_keysym());
1050 EXPECT_EQ(kTestIBusKeyCode1,
1051 mock_ime_engine_handler_->last_processed_keycode());
1052 EXPECT_EQ(kTestIBusState1,
1053 mock_ime_engine_handler_->last_processed_state());
1055 KeyEventCallback first_callback =
1056 mock_ime_engine_handler_->last_passed_callback();
1058 // Set up IBusKeyEventFromNativeKeyEvent result for second key event.
1059 ime_->mutable_ibus_key_event_from_native_key_event_result()->keyval
1061 ime_->mutable_ibus_key_event_from_native_key_event_result()->keycode
1062 = kTestIBusKeyCode2;
1063 ime_->mutable_ibus_key_event_from_native_key_event_result()->state
1066 // Do key event again.
1067 ime_->DispatchKeyEvent(&event);
1068 EXPECT_EQ(kTestIBusKeyVal2,
1069 mock_ime_engine_handler_->last_processed_keysym());
1070 EXPECT_EQ(kTestIBusKeyCode2,
1071 mock_ime_engine_handler_->last_processed_keycode());
1072 EXPECT_EQ(kTestIBusState2,
1073 mock_ime_engine_handler_->last_processed_state());
1075 // Check before state.
1077 mock_ime_engine_handler_->process_key_event_call_count());
1078 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1080 // Do callback for first key event.
1081 first_callback.Run(true);
1083 // Check the results for first key event.
1084 EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
1085 EXPECT_TRUE(IsEqualXKeyEvent(event,
1086 ime_->process_key_event_post_ime_args().event));
1087 EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
1089 // Do callback for second key event.
1090 mock_ime_engine_handler_->last_passed_callback().Run(false);
1092 // Check the results for second key event.
1093 EXPECT_EQ(2, ime_->process_key_event_post_ime_call_count());
1094 EXPECT_TRUE(IsEqualXKeyEvent(event,
1095 ime_->process_key_event_post_ime_args().event));
1096 EXPECT_FALSE(ime_->process_key_event_post_ime_args().handled);
1099 TEST_F(InputMethodIBusKeyEventTest, KeyEventDelayResponseResetTest) {
1101 event.xkey.type = KeyPress;
1103 // Set up IBusKeyEventFromNativeKeyEvent result.
1104 ime_->mutable_ibus_key_event_from_native_key_event_result()->keyval
1106 ime_->mutable_ibus_key_event_from_native_key_event_result()->keycode
1107 = kTestIBusKeyCode1;
1108 ime_->mutable_ibus_key_event_from_native_key_event_result()->state
1112 input_type_ = TEXT_INPUT_TYPE_TEXT;
1113 ime_->OnTextInputTypeChanged(this);
1114 ime_->DispatchKeyEvent(&event);
1116 // Check before state.
1118 mock_ime_engine_handler_->process_key_event_call_count());
1119 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1121 ime_->ResetContext();
1124 mock_ime_engine_handler_->last_passed_callback().Run(true);
1126 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1129 // TODO(nona): Introduce ProcessKeyEventPostIME tests(crbug.com/156593).