1 // Copyright 2014 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.
5 #include "ui/base/ime/input_method_chromeos.h"
15 #include "base/i18n/char_iterator.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "chromeos/ime/composition_text.h"
19 #include "chromeos/ime/ibus_keymap.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "ui/base/ime/chromeos/ime_bridge.h"
22 #include "ui/base/ime/chromeos/mock_ime_candidate_window_handler.h"
23 #include "ui/base/ime/chromeos/mock_ime_engine_handler.h"
24 #include "ui/base/ime/input_method_delegate.h"
25 #include "ui/base/ime/text_input_client.h"
26 #include "ui/events/event.h"
27 #include "ui/events/test/events_test_utils_x11.h"
28 #include "ui/gfx/geometry/rect.h"
30 using base::UTF8ToUTF16;
31 using base::UTF16ToUTF8;
35 typedef chromeos::IMEEngineHandlerInterface::KeyEventDoneCallback
38 uint32 GetOffsetInUTF16(
39 const base::string16& utf16_string, uint32 utf8_offset) {
40 DCHECK_LT(utf8_offset, utf16_string.size());
41 base::i18n::UTF16CharIterator char_iterator(&utf16_string);
42 for (size_t i = 0; i < utf8_offset; ++i)
43 char_iterator.Advance();
44 return char_iterator.array_pos();
47 bool IsEqualXKeyEvent(const XEvent& e1, const XEvent& e2) {
48 if ((e1.type == KeyPress && e2.type == KeyPress) ||
49 (e1.type == KeyRelease && e2.type == KeyRelease)) {
50 return !std::memcmp(&e1.xkey, &e2.xkey, sizeof(XKeyEvent));
55 enum KeyEventHandlerBehavior {
63 class TestableInputMethodChromeOS : public InputMethodChromeOS {
65 explicit TestableInputMethodChromeOS(internal::InputMethodDelegate* delegate)
66 : InputMethodChromeOS(delegate),
67 process_key_event_post_ime_call_count_(0) {
70 struct ProcessKeyEventPostIMEArgs {
71 ProcessKeyEventPostIMEArgs() : event(NULL), handled(false) {}
72 const ui::KeyEvent* event;
76 // Overridden from InputMethodChromeOS:
77 virtual void ProcessKeyEventPostIME(const ui::KeyEvent& key_event,
78 bool handled) OVERRIDE {
79 process_key_event_post_ime_args_.event = &key_event;
80 process_key_event_post_ime_args_.handled = handled;
81 ++process_key_event_post_ime_call_count_;
84 void ResetCallCount() {
85 process_key_event_post_ime_call_count_ = 0;
88 const ProcessKeyEventPostIMEArgs& process_key_event_post_ime_args() const {
89 return process_key_event_post_ime_args_;
92 int process_key_event_post_ime_call_count() const {
93 return process_key_event_post_ime_call_count_;
96 // Change access rights for testing.
97 using InputMethodChromeOS::ExtractCompositionText;
98 using InputMethodChromeOS::ResetContext;
101 ProcessKeyEventPostIMEArgs process_key_event_post_ime_args_;
102 int process_key_event_post_ime_call_count_;
105 class SynchronousKeyEventHandler {
107 SynchronousKeyEventHandler(uint32 expected_keyval,
108 uint32 expected_keycode,
109 uint32 expected_state,
110 KeyEventHandlerBehavior behavior)
111 : expected_keyval_(expected_keyval),
112 expected_keycode_(expected_keycode),
113 expected_state_(expected_state),
114 behavior_(behavior) {}
116 virtual ~SynchronousKeyEventHandler() {}
118 void Run(uint32 keyval,
121 const KeyEventCallback& callback) {
122 EXPECT_EQ(expected_keyval_, keyval);
123 EXPECT_EQ(expected_keycode_, keycode);
124 EXPECT_EQ(expected_state_, state);
125 callback.Run(behavior_ == KEYEVENT_CONSUME);
129 const uint32 expected_keyval_;
130 const uint32 expected_keycode_;
131 const uint32 expected_state_;
132 const KeyEventHandlerBehavior behavior_;
134 DISALLOW_COPY_AND_ASSIGN(SynchronousKeyEventHandler);
137 class AsynchronousKeyEventHandler {
139 AsynchronousKeyEventHandler(uint32 expected_keyval,
140 uint32 expected_keycode,
141 uint32 expected_state)
142 : expected_keyval_(expected_keyval),
143 expected_keycode_(expected_keycode),
144 expected_state_(expected_state) {}
146 virtual ~AsynchronousKeyEventHandler() {}
148 void Run(uint32 keyval,
151 const KeyEventCallback& callback) {
152 EXPECT_EQ(expected_keyval_, keyval);
153 EXPECT_EQ(expected_keycode_, keycode);
154 EXPECT_EQ(expected_state_, state);
155 callback_ = callback;
158 void RunCallback(KeyEventHandlerBehavior behavior) {
159 callback_.Run(behavior == KEYEVENT_CONSUME);
163 const uint32 expected_keyval_;
164 const uint32 expected_keycode_;
165 const uint32 expected_state_;
166 KeyEventCallback callback_;
168 DISALLOW_COPY_AND_ASSIGN(AsynchronousKeyEventHandler);
171 class SetSurroundingTextVerifier {
173 SetSurroundingTextVerifier(const std::string& expected_surrounding_text,
174 uint32 expected_cursor_position,
175 uint32 expected_anchor_position)
176 : expected_surrounding_text_(expected_surrounding_text),
177 expected_cursor_position_(expected_cursor_position),
178 expected_anchor_position_(expected_anchor_position) {}
180 void Verify(const std::string& text,
183 EXPECT_EQ(expected_surrounding_text_, text);
184 EXPECT_EQ(expected_cursor_position_, cursor_pos);
185 EXPECT_EQ(expected_anchor_position_, anchor_pos);
189 const std::string expected_surrounding_text_;
190 const uint32 expected_cursor_position_;
191 const uint32 expected_anchor_position_;
193 DISALLOW_COPY_AND_ASSIGN(SetSurroundingTextVerifier);
196 class InputMethodChromeOSTest : public internal::InputMethodDelegate,
197 public testing::Test,
198 public TextInputClient {
200 InputMethodChromeOSTest()
201 : dispatched_key_event_(ui::ET_UNKNOWN, ui::VKEY_UNKNOWN, 0, false) {
205 virtual ~InputMethodChromeOSTest() {
208 virtual void SetUp() OVERRIDE {
209 chromeos::IMEBridge::Initialize();
211 mock_ime_engine_handler_.reset(
212 new chromeos::MockIMEEngineHandler());
213 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(
214 mock_ime_engine_handler_.get());
216 mock_ime_candidate_window_handler_.reset(
217 new chromeos::MockIMECandidateWindowHandler());
218 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(
219 mock_ime_candidate_window_handler_.get());
221 ime_.reset(new TestableInputMethodChromeOS(this));
222 ime_->SetFocusedTextInputClient(this);
225 virtual void TearDown() OVERRIDE {
227 ime_->SetFocusedTextInputClient(NULL);
229 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(NULL);
230 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(NULL);
231 mock_ime_engine_handler_.reset();
232 mock_ime_candidate_window_handler_.reset();
233 chromeos::IMEBridge::Shutdown();
236 // Overridden from ui::internal::InputMethodDelegate:
237 virtual bool DispatchKeyEventPostIME(const ui::KeyEvent& event) OVERRIDE {
238 dispatched_key_event_ = event;
242 // Overridden from ui::TextInputClient:
243 virtual void SetCompositionText(
244 const CompositionText& composition) OVERRIDE {
245 composition_text_ = composition;
247 virtual void ConfirmCompositionText() OVERRIDE {
248 confirmed_text_ = composition_text_;
249 composition_text_.Clear();
251 virtual void ClearCompositionText() OVERRIDE {
252 composition_text_.Clear();
254 virtual void InsertText(const base::string16& text) OVERRIDE {
255 inserted_text_ = text;
257 virtual void InsertChar(base::char16 ch, int flags) OVERRIDE {
259 inserted_char_flags_ = flags;
261 virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE {
262 return static_cast<gfx::NativeWindow>(NULL);
264 virtual TextInputType GetTextInputType() const OVERRIDE {
267 virtual TextInputMode GetTextInputMode() const OVERRIDE {
270 virtual bool CanComposeInline() const OVERRIDE {
271 return can_compose_inline_;
273 virtual gfx::Rect GetCaretBounds() const OVERRIDE {
274 return caret_bounds_;
276 virtual bool GetCompositionCharacterBounds(uint32 index,
277 gfx::Rect* rect) const OVERRIDE {
280 virtual bool HasCompositionText() const OVERRIDE {
281 CompositionText empty;
282 return composition_text_ != empty;
284 virtual bool GetTextRange(gfx::Range* range) const OVERRIDE {
285 *range = text_range_;
288 virtual bool GetCompositionTextRange(gfx::Range* range) const OVERRIDE {
291 virtual bool GetSelectionRange(gfx::Range* range) const OVERRIDE {
292 *range = selection_range_;
296 virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE {
299 virtual bool DeleteRange(const gfx::Range& range) OVERRIDE { return false; }
300 virtual bool GetTextFromRange(const gfx::Range& range,
301 base::string16* text) const OVERRIDE {
302 *text = surrounding_text_.substr(range.GetMin(), range.length());
305 virtual void OnInputMethodChanged() OVERRIDE {
306 ++on_input_method_changed_call_count_;
308 virtual bool ChangeTextDirectionAndLayoutAlignment(
309 base::i18n::TextDirection direction) OVERRIDE { return false; }
310 virtual void ExtendSelectionAndDelete(size_t before,
311 size_t after) OVERRIDE {}
312 virtual void EnsureCaretInRect(const gfx::Rect& rect) OVERRIDE {}
313 virtual void OnCandidateWindowShown() OVERRIDE {}
314 virtual void OnCandidateWindowUpdated() OVERRIDE {}
315 virtual void OnCandidateWindowHidden() OVERRIDE {}
317 bool HasNativeEvent() const {
318 return dispatched_key_event_.HasNativeEvent();
322 dispatched_key_event_ = ui::KeyEvent(ui::ET_UNKNOWN, ui::VKEY_UNKNOWN, 0,
325 composition_text_.Clear();
326 confirmed_text_.Clear();
327 inserted_text_.clear();
329 inserted_char_flags_ = 0;
330 on_input_method_changed_call_count_ = 0;
332 input_type_ = TEXT_INPUT_TYPE_NONE;
333 input_mode_ = TEXT_INPUT_MODE_DEFAULT;
334 can_compose_inline_ = true;
335 caret_bounds_ = gfx::Rect();
338 scoped_ptr<TestableInputMethodChromeOS> ime_;
340 // Copy of the dispatched key event.
341 ui::KeyEvent dispatched_key_event_;
343 // Variables for remembering the parameters that are passed to
344 // ui::TextInputClient functions.
345 CompositionText composition_text_;
346 CompositionText confirmed_text_;
347 base::string16 inserted_text_;
348 base::char16 inserted_char_;
349 unsigned int on_input_method_changed_call_count_;
350 int inserted_char_flags_;
352 // Variables that will be returned from the ui::TextInputClient functions.
353 TextInputType input_type_;
354 TextInputMode input_mode_;
355 bool can_compose_inline_;
356 gfx::Rect caret_bounds_;
357 gfx::Range text_range_;
358 gfx::Range selection_range_;
359 base::string16 surrounding_text_;
361 scoped_ptr<chromeos::MockIMEEngineHandler> mock_ime_engine_handler_;
362 scoped_ptr<chromeos::MockIMECandidateWindowHandler>
363 mock_ime_candidate_window_handler_;
365 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSTest);
368 // Tests public APIs in ui::InputMethod first.
370 TEST_F(InputMethodChromeOSTest, GetInputLocale) {
371 // ui::InputMethodChromeOS does not support the API.
373 EXPECT_EQ("", ime_->GetInputLocale());
376 TEST_F(InputMethodChromeOSTest, IsActive) {
378 // ui::InputMethodChromeOS always returns true.
379 EXPECT_TRUE(ime_->IsActive());
382 TEST_F(InputMethodChromeOSTest, GetInputTextType) {
384 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
385 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
386 ime_->OnTextInputTypeChanged(this);
387 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
388 input_type_ = TEXT_INPUT_TYPE_TEXT;
389 ime_->OnTextInputTypeChanged(this);
390 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT, ime_->GetTextInputType());
393 TEST_F(InputMethodChromeOSTest, CanComposeInline) {
395 EXPECT_TRUE(ime_->CanComposeInline());
396 can_compose_inline_ = false;
397 ime_->OnTextInputTypeChanged(this);
398 EXPECT_FALSE(ime_->CanComposeInline());
401 TEST_F(InputMethodChromeOSTest, GetTextInputClient) {
403 EXPECT_EQ(this, ime_->GetTextInputClient());
404 ime_->SetFocusedTextInputClient(NULL);
405 EXPECT_EQ(NULL, ime_->GetTextInputClient());
408 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedClient) {
410 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
411 ime_->SetFocusedTextInputClient(NULL);
412 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
413 ime_->OnTextInputTypeChanged(this);
414 // The OnTextInputTypeChanged() call above should be ignored since |this| is
415 // not the current focused client.
416 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
418 ime_->SetFocusedTextInputClient(this);
419 ime_->OnTextInputTypeChanged(this);
420 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
423 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow) {
425 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
427 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
428 ime_->OnTextInputTypeChanged(this);
429 // The OnTextInputTypeChanged() call above should be ignored since the top-
430 // level window which the ime_ is attached to is not currently focused.
431 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
434 ime_->OnTextInputTypeChanged(this);
435 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
438 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow2) {
439 ime_->Init(false); // the top-level is initially unfocused.
440 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
441 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
442 ime_->OnTextInputTypeChanged(this);
443 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
446 ime_->OnTextInputTypeChanged(this);
447 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
450 // Confirm that IBusClient::FocusIn is called on "connected" if input_type_ is
452 TEST_F(InputMethodChromeOSTest, FocusIn_Text) {
454 // A context shouldn't be created since the daemon is not running.
455 EXPECT_EQ(0U, on_input_method_changed_call_count_);
456 // Click a text input form.
457 input_type_ = TEXT_INPUT_TYPE_TEXT;
458 ime_->OnTextInputTypeChanged(this);
459 // Since a form has focus, IBusClient::FocusIn() should be called.
460 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
463 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
464 // ui::TextInputClient::OnInputMethodChanged() should be called when
465 // ui::InputMethodChromeOS connects/disconnects to/from ibus-daemon and the
466 // current text input type is not NONE.
467 EXPECT_EQ(1U, on_input_method_changed_call_count_);
470 // Confirm that IBusClient::FocusIn is NOT called on "connected" if input_type_
472 TEST_F(InputMethodChromeOSTest, FocusIn_Password) {
474 EXPECT_EQ(0U, on_input_method_changed_call_count_);
475 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
476 ime_->OnTextInputTypeChanged(this);
477 // Since a form has focus, IBusClient::FocusIn() should NOT be called.
478 EXPECT_EQ(0, mock_ime_engine_handler_->focus_in_call_count());
479 EXPECT_EQ(1U, on_input_method_changed_call_count_);
482 // Confirm that IBusClient::FocusOut is called as expected.
483 TEST_F(InputMethodChromeOSTest, FocusOut_None) {
484 input_type_ = TEXT_INPUT_TYPE_TEXT;
486 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
487 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
488 input_type_ = TEXT_INPUT_TYPE_NONE;
489 ime_->OnTextInputTypeChanged(this);
490 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
491 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
494 // Confirm that IBusClient::FocusOut is called as expected.
495 TEST_F(InputMethodChromeOSTest, FocusOut_Password) {
496 input_type_ = TEXT_INPUT_TYPE_TEXT;
498 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
499 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
500 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
501 ime_->OnTextInputTypeChanged(this);
502 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
503 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
506 // FocusIn/FocusOut scenario test
507 TEST_F(InputMethodChromeOSTest, Focus_Scenario) {
509 // Confirm that both FocusIn and FocusOut are NOT called.
510 EXPECT_EQ(0, mock_ime_engine_handler_->focus_in_call_count());
511 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
512 EXPECT_EQ(TEXT_INPUT_TYPE_NONE,
513 mock_ime_engine_handler_->last_text_input_context().type);
514 EXPECT_EQ(TEXT_INPUT_MODE_DEFAULT,
515 mock_ime_engine_handler_->last_text_input_context().mode);
517 input_type_ = TEXT_INPUT_TYPE_TEXT;
518 input_mode_ = TEXT_INPUT_MODE_LATIN;
519 ime_->OnTextInputTypeChanged(this);
520 // Confirm that only FocusIn is called, the TextInputType is TEXT and the
521 // TextInputMode is LATIN..
522 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
523 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
524 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT,
525 mock_ime_engine_handler_->last_text_input_context().type);
526 EXPECT_EQ(TEXT_INPUT_MODE_LATIN,
527 mock_ime_engine_handler_->last_text_input_context().mode);
529 input_mode_ = TEXT_INPUT_MODE_KANA;
530 ime_->OnTextInputTypeChanged(this);
531 // Confirm that both FocusIn and FocusOut are NOT called.
532 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
533 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
534 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT,
535 mock_ime_engine_handler_->last_text_input_context().type);
536 EXPECT_EQ(TEXT_INPUT_MODE_LATIN,
537 mock_ime_engine_handler_->last_text_input_context().mode);
539 input_type_ = TEXT_INPUT_TYPE_URL;
540 ime_->OnTextInputTypeChanged(this);
541 // Confirm that both FocusIn and FocusOut are called and the TextInputType is
543 EXPECT_EQ(2, mock_ime_engine_handler_->focus_in_call_count());
544 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
545 EXPECT_EQ(TEXT_INPUT_TYPE_URL,
546 mock_ime_engine_handler_->last_text_input_context().type);
547 EXPECT_EQ(TEXT_INPUT_MODE_KANA,
548 mock_ime_engine_handler_->last_text_input_context().mode);
551 // Test if the new |caret_bounds_| is correctly sent to ibus-daemon.
552 TEST_F(InputMethodChromeOSTest, OnCaretBoundsChanged) {
553 input_type_ = TEXT_INPUT_TYPE_TEXT;
557 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
558 caret_bounds_ = gfx::Rect(1, 2, 3, 4);
559 ime_->OnCaretBoundsChanged(this);
562 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
563 caret_bounds_ = gfx::Rect(0, 2, 3, 4);
564 ime_->OnCaretBoundsChanged(this);
567 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
568 caret_bounds_ = gfx::Rect(0, 2, 3, 4); // unchanged
569 ime_->OnCaretBoundsChanged(this);
570 // Current InputMethodChromeOS implementation performs the IPC
571 // regardless of the bounds are changed or not.
574 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
577 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_NoAttribute) {
578 const base::string16 kSampleText = base::UTF8ToUTF16("Sample Text");
579 const uint32 kCursorPos = 2UL;
581 chromeos::CompositionText chromeos_composition_text;
582 chromeos_composition_text.set_text(kSampleText);
584 CompositionText composition_text;
585 ime_->ExtractCompositionText(
586 chromeos_composition_text, kCursorPos, &composition_text);
587 EXPECT_EQ(kSampleText, composition_text.text);
588 // If there is no selection, |selection| represents cursor position.
589 EXPECT_EQ(kCursorPos, composition_text.selection.start());
590 EXPECT_EQ(kCursorPos, composition_text.selection.end());
591 // If there is no underline, |underlines| contains one underline and it is
592 // whole text underline.
593 ASSERT_EQ(1UL, composition_text.underlines.size());
594 EXPECT_EQ(0UL, composition_text.underlines[0].start_offset);
595 EXPECT_EQ(kSampleText.size(), composition_text.underlines[0].end_offset);
596 EXPECT_FALSE(composition_text.underlines[0].thick);
599 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_SingleUnderline) {
600 const base::string16 kSampleText = base::UTF8ToUTF16(
601 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
602 const uint32 kCursorPos = 2UL;
604 // Set up chromeos composition text with one underline attribute.
605 chromeos::CompositionText chromeos_composition_text;
606 chromeos_composition_text.set_text(kSampleText);
607 chromeos::CompositionText::UnderlineAttribute underline;
608 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_SINGLE;
609 underline.start_index = 1UL;
610 underline.end_index = 4UL;
611 chromeos_composition_text.mutable_underline_attributes()->push_back(
614 CompositionText composition_text;
615 ime_->ExtractCompositionText(
616 chromeos_composition_text, kCursorPos, &composition_text);
617 EXPECT_EQ(kSampleText, composition_text.text);
618 // If there is no selection, |selection| represents cursor position.
619 EXPECT_EQ(kCursorPos, composition_text.selection.start());
620 EXPECT_EQ(kCursorPos, composition_text.selection.end());
621 ASSERT_EQ(1UL, composition_text.underlines.size());
622 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
623 composition_text.underlines[0].start_offset);
624 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
625 composition_text.underlines[0].end_offset);
626 // Single underline represents as black thin line.
627 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
628 EXPECT_FALSE(composition_text.underlines[0].thick);
631 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_DoubleUnderline) {
632 const base::string16 kSampleText = base::UTF8ToUTF16(
633 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
634 const uint32 kCursorPos = 2UL;
636 // Set up chromeos composition text with one underline attribute.
637 chromeos::CompositionText chromeos_composition_text;
638 chromeos_composition_text.set_text(kSampleText);
639 chromeos::CompositionText::UnderlineAttribute underline;
640 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_DOUBLE;
641 underline.start_index = 1UL;
642 underline.end_index = 4UL;
643 chromeos_composition_text.mutable_underline_attributes()->push_back(
646 CompositionText composition_text;
647 ime_->ExtractCompositionText(
648 chromeos_composition_text, kCursorPos, &composition_text);
649 EXPECT_EQ(kSampleText, composition_text.text);
650 // If there is no selection, |selection| represents cursor position.
651 EXPECT_EQ(kCursorPos, composition_text.selection.start());
652 EXPECT_EQ(kCursorPos, composition_text.selection.end());
653 ASSERT_EQ(1UL, composition_text.underlines.size());
654 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
655 composition_text.underlines[0].start_offset);
656 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
657 composition_text.underlines[0].end_offset);
658 // Double underline represents as black thick line.
659 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
660 EXPECT_TRUE(composition_text.underlines[0].thick);
663 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_ErrorUnderline) {
664 const base::string16 kSampleText = base::UTF8ToUTF16(
665 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
666 const uint32 kCursorPos = 2UL;
668 // Set up chromeos composition text with one underline attribute.
669 chromeos::CompositionText chromeos_composition_text;
670 chromeos_composition_text.set_text(kSampleText);
671 chromeos::CompositionText::UnderlineAttribute underline;
672 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_ERROR;
673 underline.start_index = 1UL;
674 underline.end_index = 4UL;
675 chromeos_composition_text.mutable_underline_attributes()->push_back(
678 CompositionText composition_text;
679 ime_->ExtractCompositionText(
680 chromeos_composition_text, kCursorPos, &composition_text);
681 EXPECT_EQ(kSampleText, composition_text.text);
682 EXPECT_EQ(kCursorPos, composition_text.selection.start());
683 EXPECT_EQ(kCursorPos, composition_text.selection.end());
684 ASSERT_EQ(1UL, composition_text.underlines.size());
685 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
686 composition_text.underlines[0].start_offset);
687 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
688 composition_text.underlines[0].end_offset);
689 // Error underline represents as red thin line.
690 EXPECT_EQ(SK_ColorRED, composition_text.underlines[0].color);
691 EXPECT_FALSE(composition_text.underlines[0].thick);
694 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_Selection) {
695 const base::string16 kSampleText = base::UTF8ToUTF16(
696 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
697 const uint32 kCursorPos = 2UL;
699 // Set up chromeos composition text with one underline attribute.
700 chromeos::CompositionText chromeos_composition_text;
701 chromeos_composition_text.set_text(kSampleText);
702 chromeos_composition_text.set_selection_start(1UL);
703 chromeos_composition_text.set_selection_end(4UL);
705 CompositionText composition_text;
706 ime_->ExtractCompositionText(
707 chromeos_composition_text, kCursorPos, &composition_text);
708 EXPECT_EQ(kSampleText, composition_text.text);
709 EXPECT_EQ(kCursorPos, composition_text.selection.start());
710 EXPECT_EQ(kCursorPos, composition_text.selection.end());
711 ASSERT_EQ(1UL, composition_text.underlines.size());
712 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
713 chromeos_composition_text.selection_start()),
714 composition_text.underlines[0].start_offset);
715 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
716 chromeos_composition_text.selection_end()),
717 composition_text.underlines[0].end_offset);
718 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
719 EXPECT_TRUE(composition_text.underlines[0].thick);
722 TEST_F(InputMethodChromeOSTest,
723 ExtractCompositionTextTest_SelectionStartWithCursor) {
724 const base::string16 kSampleText = base::UTF8ToUTF16(
725 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
726 const uint32 kCursorPos = 1UL;
728 // Set up chromeos composition text with one underline attribute.
729 chromeos::CompositionText chromeos_composition_text;
730 chromeos_composition_text.set_text(kSampleText);
731 chromeos_composition_text.set_selection_start(kCursorPos);
732 chromeos_composition_text.set_selection_end(4UL);
734 CompositionText composition_text;
735 ime_->ExtractCompositionText(
736 chromeos_composition_text, kCursorPos, &composition_text);
737 EXPECT_EQ(kSampleText, composition_text.text);
738 // If the cursor position is same as selection bounds, selection start
739 // position become opposit side of selection from cursor.
740 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
741 chromeos_composition_text.selection_end()),
742 composition_text.selection.start());
743 EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
744 composition_text.selection.end());
745 ASSERT_EQ(1UL, composition_text.underlines.size());
746 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
747 chromeos_composition_text.selection_start()),
748 composition_text.underlines[0].start_offset);
749 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
750 chromeos_composition_text.selection_end()),
751 composition_text.underlines[0].end_offset);
752 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
753 EXPECT_TRUE(composition_text.underlines[0].thick);
756 TEST_F(InputMethodChromeOSTest,
757 ExtractCompositionTextTest_SelectionEndWithCursor) {
758 const base::string16 kSampleText = base::UTF8ToUTF16(
759 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
760 const uint32 kCursorPos = 4UL;
762 // Set up chromeos composition text with one underline attribute.
763 chromeos::CompositionText chromeos_composition_text;
764 chromeos_composition_text.set_text(kSampleText);
765 chromeos_composition_text.set_selection_start(1UL);
766 chromeos_composition_text.set_selection_end(kCursorPos);
768 CompositionText composition_text;
769 ime_->ExtractCompositionText(
770 chromeos_composition_text, kCursorPos, &composition_text);
771 EXPECT_EQ(kSampleText, composition_text.text);
772 // If the cursor position is same as selection bounds, selection start
773 // position become opposit side of selection from cursor.
774 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
775 chromeos_composition_text.selection_start()),
776 composition_text.selection.start());
777 EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
778 composition_text.selection.end());
779 ASSERT_EQ(1UL, composition_text.underlines.size());
780 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
781 chromeos_composition_text.selection_start()),
782 composition_text.underlines[0].start_offset);
783 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
784 chromeos_composition_text.selection_end()),
785 composition_text.underlines[0].end_offset);
786 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
787 EXPECT_TRUE(composition_text.underlines[0].thick);
790 TEST_F(InputMethodChromeOSTest, SurroundingText_NoSelectionTest) {
792 // Click a text input form.
793 input_type_ = TEXT_INPUT_TYPE_TEXT;
794 ime_->OnTextInputTypeChanged(this);
796 // Set the TextInputClient behaviors.
797 surrounding_text_ = UTF8ToUTF16("abcdef");
798 text_range_ = gfx::Range(0, 6);
799 selection_range_ = gfx::Range(3, 3);
801 // Set the verifier for SetSurroundingText mock call.
802 SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 3, 3);
805 ime_->OnCaretBoundsChanged(this);
807 // Check the call count.
809 mock_ime_engine_handler_->set_surrounding_text_call_count());
810 EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
811 mock_ime_engine_handler_->last_set_surrounding_text());
813 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
815 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
818 TEST_F(InputMethodChromeOSTest, SurroundingText_SelectionTest) {
820 // Click a text input form.
821 input_type_ = TEXT_INPUT_TYPE_TEXT;
822 ime_->OnTextInputTypeChanged(this);
824 // Set the TextInputClient behaviors.
825 surrounding_text_ = UTF8ToUTF16("abcdef");
826 text_range_ = gfx::Range(0, 6);
827 selection_range_ = gfx::Range(2, 5);
829 // Set the verifier for SetSurroundingText mock call.
830 SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 2, 5);
832 ime_->OnCaretBoundsChanged(this);
834 // Check the call count.
836 mock_ime_engine_handler_->set_surrounding_text_call_count());
837 EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
838 mock_ime_engine_handler_->last_set_surrounding_text());
840 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
842 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
845 TEST_F(InputMethodChromeOSTest, SurroundingText_PartialText) {
847 // Click a text input form.
848 input_type_ = TEXT_INPUT_TYPE_TEXT;
849 ime_->OnTextInputTypeChanged(this);
851 // Set the TextInputClient behaviors.
852 surrounding_text_ = base::UTF8ToUTF16("abcdefghij");
853 text_range_ = gfx::Range(5, 10);
854 selection_range_ = gfx::Range(7, 9);
856 ime_->OnCaretBoundsChanged(this);
858 // Check the call count.
860 mock_ime_engine_handler_->set_surrounding_text_call_count());
861 // Set the verifier for SetSurroundingText mock call.
862 // Here (2, 4) is selection range in expected surrounding text coordinates.
864 mock_ime_engine_handler_->last_set_surrounding_text());
866 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
868 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
871 TEST_F(InputMethodChromeOSTest, SurroundingText_BecomeEmptyText) {
873 // Click a text input form.
874 input_type_ = TEXT_INPUT_TYPE_TEXT;
875 ime_->OnTextInputTypeChanged(this);
877 // Set the TextInputClient behaviors.
878 // If the surrounding text becomes empty, text_range become (0, 0) and
879 // selection range become invalid.
880 surrounding_text_ = base::UTF8ToUTF16("");
881 text_range_ = gfx::Range(0, 0);
882 selection_range_ = gfx::Range::InvalidRange();
884 ime_->OnCaretBoundsChanged(this);
886 // Check the call count.
888 mock_ime_engine_handler_->set_surrounding_text_call_count());
890 // Should not be called twice with same condition.
891 ime_->OnCaretBoundsChanged(this);
893 mock_ime_engine_handler_->set_surrounding_text_call_count());
896 class InputMethodChromeOSKeyEventTest : public InputMethodChromeOSTest {
898 InputMethodChromeOSKeyEventTest() {}
899 virtual ~InputMethodChromeOSKeyEventTest() {}
901 virtual void SetUp() OVERRIDE {
902 InputMethodChromeOSTest::SetUp();
906 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSKeyEventTest);
909 TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseTest) {
910 const int kFlags = ui::EF_SHIFT_DOWN;
911 ScopedXI2Event xevent;
912 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, kFlags);
913 const ui::KeyEvent event(xevent, true);
916 input_type_ = TEXT_INPUT_TYPE_TEXT;
917 ime_->OnTextInputTypeChanged(this);
918 ime_->DispatchKeyEvent(event);
920 // Check before state.
921 const ui::KeyEvent* key_event =
922 mock_ime_engine_handler_->last_processed_key_event();
923 EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count());
924 EXPECT_EQ(ui::VKEY_A, key_event->key_code());
925 EXPECT_EQ("KeyA", key_event->code());
926 EXPECT_EQ(kFlags, key_event->flags());
927 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
930 mock_ime_engine_handler_->last_passed_callback().Run(true);
933 EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
934 const ui::KeyEvent* stored_event =
935 ime_->process_key_event_post_ime_args().event;
936 EXPECT_TRUE(stored_event->HasNativeEvent());
937 EXPECT_TRUE(IsEqualXKeyEvent(*xevent, *(stored_event->native_event())));
938 EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
941 TEST_F(InputMethodChromeOSKeyEventTest, MultiKeyEventDelayResponseTest) {
943 input_type_ = TEXT_INPUT_TYPE_TEXT;
944 ime_->OnTextInputTypeChanged(this);
946 const int kFlags = ui::EF_SHIFT_DOWN;
947 ScopedXI2Event xevent;
948 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_B, kFlags);
949 const ui::KeyEvent event(xevent, true);
952 ime_->DispatchKeyEvent(event);
953 const ui::KeyEvent* key_event =
954 mock_ime_engine_handler_->last_processed_key_event();
955 EXPECT_EQ(ui::VKEY_B, key_event->key_code());
956 EXPECT_EQ("KeyB", key_event->code());
957 EXPECT_EQ(kFlags, key_event->flags());
959 KeyEventCallback first_callback =
960 mock_ime_engine_handler_->last_passed_callback();
962 // Do key event again.
963 ScopedXI2Event xevent2;
964 xevent2.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_C, kFlags);
965 const ui::KeyEvent event2(xevent2, true);
967 ime_->DispatchKeyEvent(event2);
968 const ui::KeyEvent* key_event2 =
969 mock_ime_engine_handler_->last_processed_key_event();
970 EXPECT_EQ(ui::VKEY_C, key_event2->key_code());
971 EXPECT_EQ("KeyC", key_event2->code());
972 EXPECT_EQ(kFlags, key_event2->flags());
974 // Check before state.
976 mock_ime_engine_handler_->process_key_event_call_count());
977 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
979 // Do callback for first key event.
980 first_callback.Run(true);
982 // Check the results for first key event.
983 EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
984 const ui::KeyEvent* stored_event =
985 ime_->process_key_event_post_ime_args().event;
986 EXPECT_TRUE(stored_event->HasNativeEvent());
987 EXPECT_TRUE(IsEqualXKeyEvent(*xevent, *(stored_event->native_event())));
988 EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
990 // Do callback for second key event.
991 mock_ime_engine_handler_->last_passed_callback().Run(false);
993 // Check the results for second key event.
994 EXPECT_EQ(2, ime_->process_key_event_post_ime_call_count());
995 stored_event = ime_->process_key_event_post_ime_args().event;
996 EXPECT_TRUE(stored_event->HasNativeEvent());
997 EXPECT_TRUE(IsEqualXKeyEvent(*xevent2, *(stored_event->native_event())));
998 EXPECT_FALSE(ime_->process_key_event_post_ime_args().handled);
1001 TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseResetTest) {
1002 ScopedXI2Event xevent;
1003 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_SHIFT_DOWN);
1004 const ui::KeyEvent event(xevent, true);
1007 input_type_ = TEXT_INPUT_TYPE_TEXT;
1008 ime_->OnTextInputTypeChanged(this);
1009 ime_->DispatchKeyEvent(event);
1011 // Check before state.
1012 EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count());
1013 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1015 ime_->ResetContext();
1018 mock_ime_engine_handler_->last_passed_callback().Run(true);
1020 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1022 // TODO(nona): Introduce ProcessKeyEventPostIME tests(crbug.com/156593).