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.
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/ime/ibus_keymap.h"
17 #include "chromeos/ime/ibus_text.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "ui/base/ime/chromeos/ime_bridge.h"
20 #include "ui/base/ime/chromeos/mock_ime_candidate_window_handler.h"
21 #include "ui/base/ime/chromeos/mock_ime_engine_handler.h"
22 #include "ui/base/ime/input_method_chromeos.h"
23 #include "ui/base/ime/input_method_delegate.h"
24 #include "ui/base/ime/text_input_client.h"
25 #include "ui/events/event.h"
26 #include "ui/events/test/events_test_utils_x11.h"
27 #include "ui/gfx/rect.h"
29 using base::UTF8ToUTF16;
30 using base::UTF16ToUTF8;
34 typedef chromeos::IMEEngineHandlerInterface::KeyEventDoneCallback
37 uint32 GetOffsetInUTF16(const std::string& utf8_string, uint32 utf8_offset) {
38 base::string16 utf16_string = UTF8ToUTF16(utf8_string);
39 DCHECK_LT(utf8_offset, utf16_string.size());
40 base::i18n::UTF16CharIterator char_iterator(&utf16_string);
41 for (size_t i = 0; i < utf8_offset; ++i)
42 char_iterator.Advance();
43 return char_iterator.array_pos();
46 bool IsEqualXKeyEvent(const XEvent& e1, const XEvent& e2) {
47 if ((e1.type == KeyPress && e2.type == KeyPress) ||
48 (e1.type == KeyRelease && e2.type == KeyRelease)) {
49 return !std::memcmp(&e1.xkey, &e2.xkey, sizeof(XKeyEvent));
54 enum KeyEventHandlerBehavior {
62 class TestableInputMethodChromeOS : public InputMethodChromeOS {
64 explicit TestableInputMethodChromeOS(internal::InputMethodDelegate* delegate)
65 : InputMethodChromeOS(delegate),
66 process_key_event_post_ime_call_count_(0) {
69 struct ProcessKeyEventPostIMEArgs {
70 ProcessKeyEventPostIMEArgs() : event(NULL), handled(false) {}
71 const ui::KeyEvent* event;
75 // InputMethodChromeOS override.
76 virtual void ProcessKeyEventPostIME(const ui::KeyEvent& key_event,
77 bool handled) OVERRIDE {
78 process_key_event_post_ime_args_.event = &key_event;
79 process_key_event_post_ime_args_.handled = handled;
80 ++process_key_event_post_ime_call_count_;
83 void ResetCallCount() {
84 process_key_event_post_ime_call_count_ = 0;
87 const ProcessKeyEventPostIMEArgs& process_key_event_post_ime_args() const {
88 return process_key_event_post_ime_args_;
91 int process_key_event_post_ime_call_count() const {
92 return process_key_event_post_ime_call_count_;
95 // Change access rights for testing.
96 using InputMethodChromeOS::ExtractCompositionText;
97 using InputMethodChromeOS::ResetContext;
100 ProcessKeyEventPostIMEArgs process_key_event_post_ime_args_;
101 int process_key_event_post_ime_call_count_;
104 class SynchronousKeyEventHandler {
106 SynchronousKeyEventHandler(uint32 expected_keyval,
107 uint32 expected_keycode,
108 uint32 expected_state,
109 KeyEventHandlerBehavior behavior)
110 : expected_keyval_(expected_keyval),
111 expected_keycode_(expected_keycode),
112 expected_state_(expected_state),
113 behavior_(behavior) {}
115 virtual ~SynchronousKeyEventHandler() {}
117 void Run(uint32 keyval,
120 const KeyEventCallback& callback) {
121 EXPECT_EQ(expected_keyval_, keyval);
122 EXPECT_EQ(expected_keycode_, keycode);
123 EXPECT_EQ(expected_state_, state);
124 callback.Run(behavior_ == KEYEVENT_CONSUME);
128 const uint32 expected_keyval_;
129 const uint32 expected_keycode_;
130 const uint32 expected_state_;
131 const KeyEventHandlerBehavior behavior_;
133 DISALLOW_COPY_AND_ASSIGN(SynchronousKeyEventHandler);
136 class AsynchronousKeyEventHandler {
138 AsynchronousKeyEventHandler(uint32 expected_keyval,
139 uint32 expected_keycode,
140 uint32 expected_state)
141 : expected_keyval_(expected_keyval),
142 expected_keycode_(expected_keycode),
143 expected_state_(expected_state) {}
145 virtual ~AsynchronousKeyEventHandler() {}
147 void Run(uint32 keyval,
150 const KeyEventCallback& callback) {
151 EXPECT_EQ(expected_keyval_, keyval);
152 EXPECT_EQ(expected_keycode_, keycode);
153 EXPECT_EQ(expected_state_, state);
154 callback_ = callback;
157 void RunCallback(KeyEventHandlerBehavior behavior) {
158 callback_.Run(behavior == KEYEVENT_CONSUME);
162 const uint32 expected_keyval_;
163 const uint32 expected_keycode_;
164 const uint32 expected_state_;
165 KeyEventCallback callback_;
167 DISALLOW_COPY_AND_ASSIGN(AsynchronousKeyEventHandler);
170 class SetSurroundingTextVerifier {
172 SetSurroundingTextVerifier(const std::string& expected_surrounding_text,
173 uint32 expected_cursor_position,
174 uint32 expected_anchor_position)
175 : expected_surrounding_text_(expected_surrounding_text),
176 expected_cursor_position_(expected_cursor_position),
177 expected_anchor_position_(expected_anchor_position) {}
179 void Verify(const std::string& text,
182 EXPECT_EQ(expected_surrounding_text_, text);
183 EXPECT_EQ(expected_cursor_position_, cursor_pos);
184 EXPECT_EQ(expected_anchor_position_, anchor_pos);
188 const std::string expected_surrounding_text_;
189 const uint32 expected_cursor_position_;
190 const uint32 expected_anchor_position_;
192 DISALLOW_COPY_AND_ASSIGN(SetSurroundingTextVerifier);
195 class InputMethodChromeOSTest : public internal::InputMethodDelegate,
196 public testing::Test,
197 public TextInputClient {
199 InputMethodChromeOSTest() {
203 virtual ~InputMethodChromeOSTest() {
206 // testing::Test overrides:
207 virtual void SetUp() OVERRIDE {
208 chromeos::IMEBridge::Initialize();
210 mock_ime_engine_handler_.reset(
211 new chromeos::MockIMEEngineHandler());
212 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(
213 mock_ime_engine_handler_.get());
215 mock_ime_candidate_window_handler_.reset(
216 new chromeos::MockIMECandidateWindowHandler());
217 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(
218 mock_ime_candidate_window_handler_.get());
220 ime_.reset(new TestableInputMethodChromeOS(this));
221 ime_->SetFocusedTextInputClient(this);
224 virtual void TearDown() OVERRIDE {
226 ime_->SetFocusedTextInputClient(NULL);
228 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(NULL);
229 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(NULL);
230 mock_ime_engine_handler_.reset();
231 mock_ime_candidate_window_handler_.reset();
232 chromeos::IMEBridge::Shutdown();
235 // ui::internal::InputMethodDelegate overrides:
236 virtual bool DispatchKeyEventPostIME(
237 const base::NativeEvent& native_key_event) OVERRIDE {
238 dispatched_native_event_ = native_key_event;
241 virtual bool DispatchFabricatedKeyEventPostIME(ui::EventType type,
242 ui::KeyboardCode key_code,
243 int flags) OVERRIDE {
244 dispatched_fabricated_event_type_ = type;
245 dispatched_fabricated_event_key_code_ = key_code;
246 dispatched_fabricated_event_flags_ = flags;
250 // ui::TextInputClient overrides:
251 virtual void SetCompositionText(
252 const CompositionText& composition) OVERRIDE {
253 composition_text_ = composition;
255 virtual void ConfirmCompositionText() OVERRIDE {
256 confirmed_text_ = composition_text_;
257 composition_text_.Clear();
259 virtual void ClearCompositionText() OVERRIDE {
260 composition_text_.Clear();
262 virtual void InsertText(const base::string16& text) OVERRIDE {
263 inserted_text_ = text;
265 virtual void InsertChar(base::char16 ch, int flags) OVERRIDE {
267 inserted_char_flags_ = flags;
269 virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE {
270 return static_cast<gfx::NativeWindow>(NULL);
272 virtual TextInputType GetTextInputType() const OVERRIDE {
275 virtual TextInputMode GetTextInputMode() const OVERRIDE {
278 virtual bool CanComposeInline() const OVERRIDE {
279 return can_compose_inline_;
281 virtual gfx::Rect GetCaretBounds() const OVERRIDE {
282 return caret_bounds_;
284 virtual bool GetCompositionCharacterBounds(uint32 index,
285 gfx::Rect* rect) const OVERRIDE {
288 virtual bool HasCompositionText() const OVERRIDE {
289 CompositionText empty;
290 return composition_text_ != empty;
292 virtual bool GetTextRange(gfx::Range* range) const OVERRIDE {
293 *range = text_range_;
296 virtual bool GetCompositionTextRange(gfx::Range* range) const OVERRIDE {
299 virtual bool GetSelectionRange(gfx::Range* range) const OVERRIDE {
300 *range = selection_range_;
304 virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE {
307 virtual bool DeleteRange(const gfx::Range& range) OVERRIDE { return false; }
308 virtual bool GetTextFromRange(const gfx::Range& range,
309 base::string16* text) const OVERRIDE {
310 *text = surrounding_text_.substr(range.GetMin(), range.length());
313 virtual void OnInputMethodChanged() OVERRIDE {
314 ++on_input_method_changed_call_count_;
316 virtual bool ChangeTextDirectionAndLayoutAlignment(
317 base::i18n::TextDirection direction) OVERRIDE { return false; }
318 virtual void ExtendSelectionAndDelete(size_t before,
319 size_t after) OVERRIDE { }
320 virtual void EnsureCaretInRect(const gfx::Rect& rect) OVERRIDE { }
321 virtual void OnCandidateWindowShown() OVERRIDE { }
322 virtual void OnCandidateWindowUpdated() OVERRIDE { }
323 virtual void OnCandidateWindowHidden() OVERRIDE { }
325 bool HasNativeEvent() const {
326 base::NativeEvent empty;
327 std::memset(&empty, 0, sizeof(empty));
328 return !!std::memcmp(&dispatched_native_event_,
330 sizeof(dispatched_native_event_));
334 std::memset(&dispatched_native_event_, 0, sizeof(dispatched_native_event_));
335 DCHECK(!HasNativeEvent());
336 dispatched_fabricated_event_type_ = ET_UNKNOWN;
337 dispatched_fabricated_event_key_code_ = VKEY_UNKNOWN;
338 dispatched_fabricated_event_flags_ = 0;
340 composition_text_.Clear();
341 confirmed_text_.Clear();
342 inserted_text_.clear();
344 inserted_char_flags_ = 0;
345 on_input_method_changed_call_count_ = 0;
347 input_type_ = TEXT_INPUT_TYPE_NONE;
348 input_mode_ = TEXT_INPUT_MODE_DEFAULT;
349 can_compose_inline_ = true;
350 caret_bounds_ = gfx::Rect();
353 scoped_ptr<TestableInputMethodChromeOS> ime_;
355 // Variables for remembering the parameters that are passed to
356 // ui::internal::InputMethodDelegate functions.
357 base::NativeEvent dispatched_native_event_;
358 ui::EventType dispatched_fabricated_event_type_;
359 ui::KeyboardCode dispatched_fabricated_event_key_code_;
360 int dispatched_fabricated_event_flags_;
362 // Variables for remembering the parameters that are passed to
363 // ui::TextInputClient functions.
364 CompositionText composition_text_;
365 CompositionText confirmed_text_;
366 base::string16 inserted_text_;
367 base::char16 inserted_char_;
368 unsigned int on_input_method_changed_call_count_;
369 int inserted_char_flags_;
371 // Variables that will be returned from the ui::TextInputClient functions.
372 TextInputType input_type_;
373 TextInputMode input_mode_;
374 bool can_compose_inline_;
375 gfx::Rect caret_bounds_;
376 gfx::Range text_range_;
377 gfx::Range selection_range_;
378 base::string16 surrounding_text_;
380 scoped_ptr<chromeos::MockIMEEngineHandler> mock_ime_engine_handler_;
381 scoped_ptr<chromeos::MockIMECandidateWindowHandler>
382 mock_ime_candidate_window_handler_;
384 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSTest);
387 // Tests public APIs in ui::InputMethod first.
389 TEST_F(InputMethodChromeOSTest, GetInputLocale) {
390 // ui::InputMethodChromeOS does not support the API.
392 EXPECT_EQ("", ime_->GetInputLocale());
395 TEST_F(InputMethodChromeOSTest, IsActive) {
397 // ui::InputMethodChromeOS always returns true.
398 EXPECT_TRUE(ime_->IsActive());
401 TEST_F(InputMethodChromeOSTest, GetInputTextType) {
403 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
404 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
405 ime_->OnTextInputTypeChanged(this);
406 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
407 input_type_ = TEXT_INPUT_TYPE_TEXT;
408 ime_->OnTextInputTypeChanged(this);
409 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT, ime_->GetTextInputType());
412 TEST_F(InputMethodChromeOSTest, CanComposeInline) {
414 EXPECT_TRUE(ime_->CanComposeInline());
415 can_compose_inline_ = false;
416 ime_->OnTextInputTypeChanged(this);
417 EXPECT_FALSE(ime_->CanComposeInline());
420 TEST_F(InputMethodChromeOSTest, GetTextInputClient) {
422 EXPECT_EQ(this, ime_->GetTextInputClient());
423 ime_->SetFocusedTextInputClient(NULL);
424 EXPECT_EQ(NULL, ime_->GetTextInputClient());
427 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedClient) {
429 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
430 ime_->SetFocusedTextInputClient(NULL);
431 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
432 ime_->OnTextInputTypeChanged(this);
433 // The OnTextInputTypeChanged() call above should be ignored since |this| is
434 // not the current focused client.
435 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
437 ime_->SetFocusedTextInputClient(this);
438 ime_->OnTextInputTypeChanged(this);
439 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
442 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow) {
444 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
446 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
447 ime_->OnTextInputTypeChanged(this);
448 // The OnTextInputTypeChanged() call above should be ignored since the top-
449 // level window which the ime_ is attached to is not currently focused.
450 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
453 ime_->OnTextInputTypeChanged(this);
454 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
457 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow2) {
458 ime_->Init(false); // the top-level is initially unfocused.
459 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
460 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
461 ime_->OnTextInputTypeChanged(this);
462 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
465 ime_->OnTextInputTypeChanged(this);
466 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
469 // Confirm that IBusClient::FocusIn is called on "connected" if input_type_ is
471 TEST_F(InputMethodChromeOSTest, FocusIn_Text) {
473 // A context shouldn't be created since the daemon is not running.
474 EXPECT_EQ(0U, on_input_method_changed_call_count_);
475 // Click a text input form.
476 input_type_ = TEXT_INPUT_TYPE_TEXT;
477 ime_->OnTextInputTypeChanged(this);
478 // Since a form has focus, IBusClient::FocusIn() should be called.
479 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
482 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
483 // ui::TextInputClient::OnInputMethodChanged() should be called when
484 // ui::InputMethodChromeOS connects/disconnects to/from ibus-daemon and the
485 // current text input type is not NONE.
486 EXPECT_EQ(1U, on_input_method_changed_call_count_);
489 // Confirm that IBusClient::FocusIn is NOT called on "connected" if input_type_
491 TEST_F(InputMethodChromeOSTest, FocusIn_Password) {
493 EXPECT_EQ(0U, on_input_method_changed_call_count_);
494 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
495 ime_->OnTextInputTypeChanged(this);
496 // Since a form has focus, IBusClient::FocusIn() should NOT be called.
497 EXPECT_EQ(0, mock_ime_engine_handler_->focus_in_call_count());
498 EXPECT_EQ(1U, on_input_method_changed_call_count_);
501 // Confirm that IBusClient::FocusOut is called as expected.
502 TEST_F(InputMethodChromeOSTest, FocusOut_None) {
503 input_type_ = TEXT_INPUT_TYPE_TEXT;
505 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
506 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
507 input_type_ = TEXT_INPUT_TYPE_NONE;
508 ime_->OnTextInputTypeChanged(this);
509 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
510 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
513 // Confirm that IBusClient::FocusOut is called as expected.
514 TEST_F(InputMethodChromeOSTest, FocusOut_Password) {
515 input_type_ = TEXT_INPUT_TYPE_TEXT;
517 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
518 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
519 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
520 ime_->OnTextInputTypeChanged(this);
521 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
522 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
525 // FocusIn/FocusOut scenario test
526 TEST_F(InputMethodChromeOSTest, Focus_Scenario) {
528 // Confirm that both FocusIn and FocusOut are NOT called.
529 EXPECT_EQ(0, mock_ime_engine_handler_->focus_in_call_count());
530 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
531 EXPECT_EQ(TEXT_INPUT_TYPE_NONE,
532 mock_ime_engine_handler_->last_text_input_context().type);
533 EXPECT_EQ(TEXT_INPUT_MODE_DEFAULT,
534 mock_ime_engine_handler_->last_text_input_context().mode);
536 input_type_ = TEXT_INPUT_TYPE_TEXT;
537 input_mode_ = TEXT_INPUT_MODE_LATIN;
538 ime_->OnTextInputTypeChanged(this);
539 // Confirm that only FocusIn is called, the TextInputType is TEXT and the
540 // TextInputMode is LATIN..
541 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
542 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
543 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT,
544 mock_ime_engine_handler_->last_text_input_context().type);
545 EXPECT_EQ(TEXT_INPUT_MODE_LATIN,
546 mock_ime_engine_handler_->last_text_input_context().mode);
548 input_mode_ = TEXT_INPUT_MODE_KANA;
549 ime_->OnTextInputTypeChanged(this);
550 // Confirm that both FocusIn and FocusOut are NOT called.
551 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
552 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
553 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT,
554 mock_ime_engine_handler_->last_text_input_context().type);
555 EXPECT_EQ(TEXT_INPUT_MODE_LATIN,
556 mock_ime_engine_handler_->last_text_input_context().mode);
558 input_type_ = TEXT_INPUT_TYPE_URL;
559 ime_->OnTextInputTypeChanged(this);
560 // Confirm that both FocusIn and FocusOut are called and the TextInputType is
562 EXPECT_EQ(2, mock_ime_engine_handler_->focus_in_call_count());
563 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
564 EXPECT_EQ(TEXT_INPUT_TYPE_URL,
565 mock_ime_engine_handler_->last_text_input_context().type);
566 EXPECT_EQ(TEXT_INPUT_MODE_KANA,
567 mock_ime_engine_handler_->last_text_input_context().mode);
570 // Test if the new |caret_bounds_| is correctly sent to ibus-daemon.
571 TEST_F(InputMethodChromeOSTest, OnCaretBoundsChanged) {
572 input_type_ = TEXT_INPUT_TYPE_TEXT;
576 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
577 caret_bounds_ = gfx::Rect(1, 2, 3, 4);
578 ime_->OnCaretBoundsChanged(this);
581 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
582 caret_bounds_ = gfx::Rect(0, 2, 3, 4);
583 ime_->OnCaretBoundsChanged(this);
586 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
587 caret_bounds_ = gfx::Rect(0, 2, 3, 4); // unchanged
588 ime_->OnCaretBoundsChanged(this);
589 // Current InputMethodChromeOS implementation performs the IPC
590 // regardless of the bounds are changed or not.
593 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
596 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_NoAttribute) {
597 const char kSampleText[] = "Sample Text";
598 const uint32 kCursorPos = 2UL;
600 const base::string16 utf16_string = UTF8ToUTF16(kSampleText);
601 chromeos::IBusText ibus_text;
602 ibus_text.set_text(kSampleText);
604 CompositionText composition_text;
605 ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
606 EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
607 // If there is no selection, |selection| represents cursor position.
608 EXPECT_EQ(kCursorPos, composition_text.selection.start());
609 EXPECT_EQ(kCursorPos, composition_text.selection.end());
610 // If there is no underline, |underlines| contains one underline and it is
611 // whole text underline.
612 ASSERT_EQ(1UL, composition_text.underlines.size());
613 EXPECT_EQ(0UL, composition_text.underlines[0].start_offset);
614 EXPECT_EQ(utf16_string.size(), composition_text.underlines[0].end_offset);
615 EXPECT_FALSE(composition_text.underlines[0].thick);
618 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_SingleUnderline) {
619 const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
620 "\xE3\x81\x88\xE3\x81\x8A";
621 const uint32 kCursorPos = 2UL;
623 // Set up ibus text with one underline attribute.
624 chromeos::IBusText ibus_text;
625 ibus_text.set_text(kSampleText);
626 chromeos::IBusText::UnderlineAttribute underline;
627 underline.type = chromeos::IBusText::IBUS_TEXT_UNDERLINE_SINGLE;
628 underline.start_index = 1UL;
629 underline.end_index = 4UL;
630 ibus_text.mutable_underline_attributes()->push_back(underline);
632 CompositionText composition_text;
633 ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
634 EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
635 // If there is no selection, |selection| represents cursor position.
636 EXPECT_EQ(kCursorPos, composition_text.selection.start());
637 EXPECT_EQ(kCursorPos, composition_text.selection.end());
638 ASSERT_EQ(1UL, composition_text.underlines.size());
639 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
640 composition_text.underlines[0].start_offset);
641 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
642 composition_text.underlines[0].end_offset);
643 // Single underline represents as black thin line.
644 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
645 EXPECT_FALSE(composition_text.underlines[0].thick);
648 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_DoubleUnderline) {
649 const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
650 "\xE3\x81\x88\xE3\x81\x8A";
651 const uint32 kCursorPos = 2UL;
653 // Set up ibus text with one underline attribute.
654 chromeos::IBusText ibus_text;
655 ibus_text.set_text(kSampleText);
656 chromeos::IBusText::UnderlineAttribute underline;
657 underline.type = chromeos::IBusText::IBUS_TEXT_UNDERLINE_DOUBLE;
658 underline.start_index = 1UL;
659 underline.end_index = 4UL;
660 ibus_text.mutable_underline_attributes()->push_back(underline);
662 CompositionText composition_text;
663 ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
664 EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
665 // If there is no selection, |selection| represents cursor position.
666 EXPECT_EQ(kCursorPos, composition_text.selection.start());
667 EXPECT_EQ(kCursorPos, composition_text.selection.end());
668 ASSERT_EQ(1UL, composition_text.underlines.size());
669 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
670 composition_text.underlines[0].start_offset);
671 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
672 composition_text.underlines[0].end_offset);
673 // Double underline represents as black thick line.
674 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
675 EXPECT_TRUE(composition_text.underlines[0].thick);
678 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_ErrorUnderline) {
679 const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
680 "\xE3\x81\x88\xE3\x81\x8A";
681 const uint32 kCursorPos = 2UL;
683 // Set up ibus text with one underline attribute.
684 chromeos::IBusText ibus_text;
685 ibus_text.set_text(kSampleText);
686 chromeos::IBusText::UnderlineAttribute underline;
687 underline.type = chromeos::IBusText::IBUS_TEXT_UNDERLINE_ERROR;
688 underline.start_index = 1UL;
689 underline.end_index = 4UL;
690 ibus_text.mutable_underline_attributes()->push_back(underline);
692 CompositionText composition_text;
693 ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
694 EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
695 EXPECT_EQ(kCursorPos, composition_text.selection.start());
696 EXPECT_EQ(kCursorPos, composition_text.selection.end());
697 ASSERT_EQ(1UL, composition_text.underlines.size());
698 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
699 composition_text.underlines[0].start_offset);
700 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
701 composition_text.underlines[0].end_offset);
702 // Error underline represents as red thin line.
703 EXPECT_EQ(SK_ColorRED, composition_text.underlines[0].color);
704 EXPECT_FALSE(composition_text.underlines[0].thick);
707 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_Selection) {
708 const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
709 "\xE3\x81\x88\xE3\x81\x8A";
710 const uint32 kCursorPos = 2UL;
712 // Set up ibus text with one underline attribute.
713 chromeos::IBusText ibus_text;
714 ibus_text.set_text(kSampleText);
715 ibus_text.set_selection_start(1UL);
716 ibus_text.set_selection_end(4UL);
718 CompositionText composition_text;
719 ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
720 EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
721 EXPECT_EQ(kCursorPos, composition_text.selection.start());
722 EXPECT_EQ(kCursorPos, composition_text.selection.end());
723 ASSERT_EQ(1UL, composition_text.underlines.size());
724 EXPECT_EQ(GetOffsetInUTF16(kSampleText, ibus_text.selection_start()),
725 composition_text.underlines[0].start_offset);
726 EXPECT_EQ(GetOffsetInUTF16(kSampleText, ibus_text.selection_end()),
727 composition_text.underlines[0].end_offset);
728 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
729 EXPECT_TRUE(composition_text.underlines[0].thick);
732 TEST_F(InputMethodChromeOSTest,
733 ExtractCompositionTextTest_SelectionStartWithCursor) {
734 const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
735 "\xE3\x81\x88\xE3\x81\x8A";
736 const uint32 kCursorPos = 1UL;
738 // Set up ibus text with one underline attribute.
739 chromeos::IBusText ibus_text;
740 ibus_text.set_text(kSampleText);
741 ibus_text.set_selection_start(kCursorPos);
742 ibus_text.set_selection_end(4UL);
744 CompositionText composition_text;
745 ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
746 EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
747 // If the cursor position is same as selection bounds, selection start
748 // position become opposit side of selection from cursor.
749 EXPECT_EQ(GetOffsetInUTF16(kSampleText, ibus_text.selection_end()),
750 composition_text.selection.start());
751 EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
752 composition_text.selection.end());
753 ASSERT_EQ(1UL, composition_text.underlines.size());
754 EXPECT_EQ(GetOffsetInUTF16(kSampleText, ibus_text.selection_start()),
755 composition_text.underlines[0].start_offset);
756 EXPECT_EQ(GetOffsetInUTF16(kSampleText, ibus_text.selection_end()),
757 composition_text.underlines[0].end_offset);
758 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
759 EXPECT_TRUE(composition_text.underlines[0].thick);
762 TEST_F(InputMethodChromeOSTest,
763 ExtractCompositionTextTest_SelectionEndWithCursor) {
764 const char kSampleText[] = "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86"
765 "\xE3\x81\x88\xE3\x81\x8A";
766 const uint32 kCursorPos = 4UL;
768 // Set up ibus text with one underline attribute.
769 chromeos::IBusText ibus_text;
770 ibus_text.set_text(kSampleText);
771 ibus_text.set_selection_start(1UL);
772 ibus_text.set_selection_end(kCursorPos);
774 CompositionText composition_text;
775 ime_->ExtractCompositionText(ibus_text, kCursorPos, &composition_text);
776 EXPECT_EQ(UTF8ToUTF16(kSampleText), composition_text.text);
777 // If the cursor position is same as selection bounds, selection start
778 // position become opposit side of selection from cursor.
779 EXPECT_EQ(GetOffsetInUTF16(kSampleText, ibus_text.selection_start()),
780 composition_text.selection.start());
781 EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
782 composition_text.selection.end());
783 ASSERT_EQ(1UL, composition_text.underlines.size());
784 EXPECT_EQ(GetOffsetInUTF16(kSampleText, ibus_text.selection_start()),
785 composition_text.underlines[0].start_offset);
786 EXPECT_EQ(GetOffsetInUTF16(kSampleText, ibus_text.selection_end()),
787 composition_text.underlines[0].end_offset);
788 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
789 EXPECT_TRUE(composition_text.underlines[0].thick);
792 TEST_F(InputMethodChromeOSTest, SurroundingText_NoSelectionTest) {
794 // Click a text input form.
795 input_type_ = TEXT_INPUT_TYPE_TEXT;
796 ime_->OnTextInputTypeChanged(this);
798 // Set the TextInputClient behaviors.
799 surrounding_text_ = UTF8ToUTF16("abcdef");
800 text_range_ = gfx::Range(0, 6);
801 selection_range_ = gfx::Range(3, 3);
803 // Set the verifier for SetSurroundingText mock call.
804 SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 3, 3);
807 ime_->OnCaretBoundsChanged(this);
809 // Check the call count.
811 mock_ime_engine_handler_->set_surrounding_text_call_count());
812 EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
813 mock_ime_engine_handler_->last_set_surrounding_text());
815 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
817 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
820 TEST_F(InputMethodChromeOSTest, SurroundingText_SelectionTest) {
822 // Click a text input form.
823 input_type_ = TEXT_INPUT_TYPE_TEXT;
824 ime_->OnTextInputTypeChanged(this);
826 // Set the TextInputClient behaviors.
827 surrounding_text_ = UTF8ToUTF16("abcdef");
828 text_range_ = gfx::Range(0, 6);
829 selection_range_ = gfx::Range(2, 5);
831 // Set the verifier for SetSurroundingText mock call.
832 SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 2, 5);
834 ime_->OnCaretBoundsChanged(this);
836 // Check the call count.
838 mock_ime_engine_handler_->set_surrounding_text_call_count());
839 EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
840 mock_ime_engine_handler_->last_set_surrounding_text());
842 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
844 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
847 TEST_F(InputMethodChromeOSTest, SurroundingText_PartialText) {
849 // Click a text input form.
850 input_type_ = TEXT_INPUT_TYPE_TEXT;
851 ime_->OnTextInputTypeChanged(this);
853 // Set the TextInputClient behaviors.
854 surrounding_text_ = UTF8ToUTF16("abcdefghij");
855 text_range_ = gfx::Range(5, 10);
856 selection_range_ = gfx::Range(7, 9);
858 ime_->OnCaretBoundsChanged(this);
860 // Check the call count.
862 mock_ime_engine_handler_->set_surrounding_text_call_count());
863 // Set the verifier for SetSurroundingText mock call.
864 // Here (2, 4) is selection range in expected surrounding text coordinates.
866 mock_ime_engine_handler_->last_set_surrounding_text());
868 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
870 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
873 TEST_F(InputMethodChromeOSTest, SurroundingText_BecomeEmptyText) {
875 // Click a text input form.
876 input_type_ = TEXT_INPUT_TYPE_TEXT;
877 ime_->OnTextInputTypeChanged(this);
879 // Set the TextInputClient behaviors.
880 // If the surrounding text becomes empty, text_range become (0, 0) and
881 // selection range become invalid.
882 surrounding_text_ = UTF8ToUTF16("");
883 text_range_ = gfx::Range(0, 0);
884 selection_range_ = gfx::Range::InvalidRange();
886 ime_->OnCaretBoundsChanged(this);
888 // Check the call count.
890 mock_ime_engine_handler_->set_surrounding_text_call_count());
892 // Should not be called twice with same condition.
893 ime_->OnCaretBoundsChanged(this);
895 mock_ime_engine_handler_->set_surrounding_text_call_count());
898 class InputMethodChromeOSKeyEventTest : public InputMethodChromeOSTest {
900 InputMethodChromeOSKeyEventTest() {}
901 virtual ~InputMethodChromeOSKeyEventTest() {}
903 virtual void SetUp() OVERRIDE {
904 InputMethodChromeOSTest::SetUp();
908 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSKeyEventTest);
911 TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseTest) {
912 const int kFlags = ui::EF_SHIFT_DOWN;
913 ScopedXI2Event xevent;
914 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, kFlags);
915 const ui::KeyEvent event(xevent, true);
918 input_type_ = TEXT_INPUT_TYPE_TEXT;
919 ime_->OnTextInputTypeChanged(this);
920 ime_->DispatchKeyEvent(event);
922 // Check before state.
923 const ui::KeyEvent* key_event =
924 mock_ime_engine_handler_->last_processed_key_event();
925 EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count());
926 EXPECT_EQ(ui::VKEY_A, key_event->key_code());
927 EXPECT_EQ("KeyA", key_event->code());
928 EXPECT_EQ(kFlags, key_event->flags());
929 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
932 mock_ime_engine_handler_->last_passed_callback().Run(true);
935 EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
936 const ui::KeyEvent* stored_event =
937 ime_->process_key_event_post_ime_args().event;
938 EXPECT_TRUE(stored_event->HasNativeEvent());
939 EXPECT_TRUE(IsEqualXKeyEvent(*xevent, *(stored_event->native_event())));
940 EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
943 TEST_F(InputMethodChromeOSKeyEventTest, MultiKeyEventDelayResponseTest) {
945 input_type_ = TEXT_INPUT_TYPE_TEXT;
946 ime_->OnTextInputTypeChanged(this);
948 const int kFlags = ui::EF_SHIFT_DOWN;
949 ScopedXI2Event xevent;
950 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_B, kFlags);
951 const ui::KeyEvent event(xevent, true);
954 ime_->DispatchKeyEvent(event);
955 const ui::KeyEvent* key_event =
956 mock_ime_engine_handler_->last_processed_key_event();
957 EXPECT_EQ(ui::VKEY_B, key_event->key_code());
958 EXPECT_EQ("KeyB", key_event->code());
959 EXPECT_EQ(kFlags, key_event->flags());
961 KeyEventCallback first_callback =
962 mock_ime_engine_handler_->last_passed_callback();
964 // Do key event again.
965 ScopedXI2Event xevent2;
966 xevent2.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_C, kFlags);
967 const ui::KeyEvent event2(xevent2, true);
969 ime_->DispatchKeyEvent(event2);
970 const ui::KeyEvent* key_event2 =
971 mock_ime_engine_handler_->last_processed_key_event();
972 EXPECT_EQ(ui::VKEY_C, key_event2->key_code());
973 EXPECT_EQ("KeyC", key_event2->code());
974 EXPECT_EQ(kFlags, key_event2->flags());
976 // Check before state.
978 mock_ime_engine_handler_->process_key_event_call_count());
979 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
981 // Do callback for first key event.
982 first_callback.Run(true);
984 // Check the results for first key event.
985 EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
986 const ui::KeyEvent* stored_event =
987 ime_->process_key_event_post_ime_args().event;
988 EXPECT_TRUE(stored_event->HasNativeEvent());
989 EXPECT_TRUE(IsEqualXKeyEvent(*xevent, *(stored_event->native_event())));
990 EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
992 // Do callback for second key event.
993 mock_ime_engine_handler_->last_passed_callback().Run(false);
995 // Check the results for second key event.
996 EXPECT_EQ(2, ime_->process_key_event_post_ime_call_count());
997 stored_event = ime_->process_key_event_post_ime_args().event;
998 EXPECT_TRUE(stored_event->HasNativeEvent());
999 EXPECT_TRUE(IsEqualXKeyEvent(*xevent2, *(stored_event->native_event())));
1000 EXPECT_FALSE(ime_->process_key_event_post_ime_args().handled);
1003 TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseResetTest) {
1004 ScopedXI2Event xevent;
1005 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_SHIFT_DOWN);
1006 const ui::KeyEvent event(xevent, true);
1009 input_type_ = TEXT_INPUT_TYPE_TEXT;
1010 ime_->OnTextInputTypeChanged(this);
1011 ime_->DispatchKeyEvent(event);
1013 // Check before state.
1014 EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count());
1015 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1017 ime_->ResetContext();
1020 mock_ime_engine_handler_->last_passed_callback().Run(true);
1022 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1024 // TODO(nona): Introduce ProcessKeyEventPostIME tests(crbug.com/156593).