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 "testing/gtest/include/gtest/gtest.h"
20 #include "ui/base/ime/chromeos/ime_bridge.h"
21 #include "ui/base/ime/chromeos/mock_ime_candidate_window_handler.h"
22 #include "ui/base/ime/chromeos/mock_ime_engine_handler.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/geometry/rect.h"
29 using base::UTF8ToUTF16;
30 using base::UTF16ToUTF8;
35 const base::string16 kSampleText = base::UTF8ToUTF16(
36 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
38 typedef chromeos::IMEEngineHandlerInterface::KeyEventDoneCallback
41 uint32 GetOffsetInUTF16(
42 const base::string16& utf16_string, uint32 utf8_offset) {
43 DCHECK_LT(utf8_offset, utf16_string.size());
44 base::i18n::UTF16CharIterator char_iterator(&utf16_string);
45 for (size_t i = 0; i < utf8_offset; ++i)
46 char_iterator.Advance();
47 return char_iterator.array_pos();
50 bool IsEqualXKeyEvent(const XEvent& e1, const XEvent& e2) {
51 if ((e1.type == KeyPress && e2.type == KeyPress) ||
52 (e1.type == KeyRelease && e2.type == KeyRelease)) {
53 return !std::memcmp(&e1.xkey, &e2.xkey, sizeof(XKeyEvent));
58 enum KeyEventHandlerBehavior {
66 class TestableInputMethodChromeOS : public InputMethodChromeOS {
68 explicit TestableInputMethodChromeOS(internal::InputMethodDelegate* delegate)
69 : InputMethodChromeOS(delegate),
70 process_key_event_post_ime_call_count_(0) {
73 struct ProcessKeyEventPostIMEArgs {
74 ProcessKeyEventPostIMEArgs() : event(NULL), handled(false) {}
75 const ui::KeyEvent* event;
79 // Overridden from InputMethodChromeOS:
80 virtual void ProcessKeyEventPostIME(const ui::KeyEvent& key_event,
81 bool handled) OVERRIDE {
82 process_key_event_post_ime_args_.event = &key_event;
83 process_key_event_post_ime_args_.handled = handled;
84 ++process_key_event_post_ime_call_count_;
87 void ResetCallCount() {
88 process_key_event_post_ime_call_count_ = 0;
91 const ProcessKeyEventPostIMEArgs& process_key_event_post_ime_args() const {
92 return process_key_event_post_ime_args_;
95 int process_key_event_post_ime_call_count() const {
96 return process_key_event_post_ime_call_count_;
99 // Change access rights for testing.
100 using InputMethodChromeOS::ExtractCompositionText;
101 using InputMethodChromeOS::ResetContext;
104 ProcessKeyEventPostIMEArgs process_key_event_post_ime_args_;
105 int process_key_event_post_ime_call_count_;
108 class SynchronousKeyEventHandler {
110 SynchronousKeyEventHandler(uint32 expected_keyval,
111 uint32 expected_keycode,
112 uint32 expected_state,
113 KeyEventHandlerBehavior behavior)
114 : expected_keyval_(expected_keyval),
115 expected_keycode_(expected_keycode),
116 expected_state_(expected_state),
117 behavior_(behavior) {}
119 virtual ~SynchronousKeyEventHandler() {}
121 void Run(uint32 keyval,
124 const KeyEventCallback& callback) {
125 EXPECT_EQ(expected_keyval_, keyval);
126 EXPECT_EQ(expected_keycode_, keycode);
127 EXPECT_EQ(expected_state_, state);
128 callback.Run(behavior_ == KEYEVENT_CONSUME);
132 const uint32 expected_keyval_;
133 const uint32 expected_keycode_;
134 const uint32 expected_state_;
135 const KeyEventHandlerBehavior behavior_;
137 DISALLOW_COPY_AND_ASSIGN(SynchronousKeyEventHandler);
140 class AsynchronousKeyEventHandler {
142 AsynchronousKeyEventHandler(uint32 expected_keyval,
143 uint32 expected_keycode,
144 uint32 expected_state)
145 : expected_keyval_(expected_keyval),
146 expected_keycode_(expected_keycode),
147 expected_state_(expected_state) {}
149 virtual ~AsynchronousKeyEventHandler() {}
151 void Run(uint32 keyval,
154 const KeyEventCallback& callback) {
155 EXPECT_EQ(expected_keyval_, keyval);
156 EXPECT_EQ(expected_keycode_, keycode);
157 EXPECT_EQ(expected_state_, state);
158 callback_ = callback;
161 void RunCallback(KeyEventHandlerBehavior behavior) {
162 callback_.Run(behavior == KEYEVENT_CONSUME);
166 const uint32 expected_keyval_;
167 const uint32 expected_keycode_;
168 const uint32 expected_state_;
169 KeyEventCallback callback_;
171 DISALLOW_COPY_AND_ASSIGN(AsynchronousKeyEventHandler);
174 class SetSurroundingTextVerifier {
176 SetSurroundingTextVerifier(const std::string& expected_surrounding_text,
177 uint32 expected_cursor_position,
178 uint32 expected_anchor_position)
179 : expected_surrounding_text_(expected_surrounding_text),
180 expected_cursor_position_(expected_cursor_position),
181 expected_anchor_position_(expected_anchor_position) {}
183 void Verify(const std::string& text,
186 EXPECT_EQ(expected_surrounding_text_, text);
187 EXPECT_EQ(expected_cursor_position_, cursor_pos);
188 EXPECT_EQ(expected_anchor_position_, anchor_pos);
192 const std::string expected_surrounding_text_;
193 const uint32 expected_cursor_position_;
194 const uint32 expected_anchor_position_;
196 DISALLOW_COPY_AND_ASSIGN(SetSurroundingTextVerifier);
199 class InputMethodChromeOSTest : public internal::InputMethodDelegate,
200 public testing::Test,
201 public TextInputClient {
203 InputMethodChromeOSTest()
204 : dispatched_key_event_(ui::ET_UNKNOWN, ui::VKEY_UNKNOWN, 0, false) {
208 virtual ~InputMethodChromeOSTest() {
211 virtual void SetUp() OVERRIDE {
212 chromeos::IMEBridge::Initialize();
214 mock_ime_engine_handler_.reset(
215 new chromeos::MockIMEEngineHandler());
216 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(
217 mock_ime_engine_handler_.get());
219 mock_ime_candidate_window_handler_.reset(
220 new chromeos::MockIMECandidateWindowHandler());
221 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(
222 mock_ime_candidate_window_handler_.get());
224 ime_.reset(new TestableInputMethodChromeOS(this));
225 ime_->SetFocusedTextInputClient(this);
228 virtual void TearDown() OVERRIDE {
230 ime_->SetFocusedTextInputClient(NULL);
232 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(NULL);
233 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(NULL);
234 mock_ime_engine_handler_.reset();
235 mock_ime_candidate_window_handler_.reset();
236 chromeos::IMEBridge::Shutdown();
239 // Overridden from ui::internal::InputMethodDelegate:
240 virtual bool DispatchKeyEventPostIME(const ui::KeyEvent& event) OVERRIDE {
241 dispatched_key_event_ = event;
245 // Overridden from ui::TextInputClient:
246 virtual void SetCompositionText(
247 const CompositionText& composition) OVERRIDE {
248 composition_text_ = composition;
250 virtual void ConfirmCompositionText() OVERRIDE {
251 confirmed_text_ = composition_text_;
252 composition_text_.Clear();
254 virtual void ClearCompositionText() OVERRIDE {
255 composition_text_.Clear();
257 virtual void InsertText(const base::string16& text) OVERRIDE {
258 inserted_text_ = text;
260 virtual void InsertChar(base::char16 ch, int flags) OVERRIDE {
262 inserted_char_flags_ = flags;
264 virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE {
265 return static_cast<gfx::NativeWindow>(NULL);
267 virtual TextInputType GetTextInputType() const OVERRIDE {
270 virtual TextInputMode GetTextInputMode() const OVERRIDE {
273 virtual bool CanComposeInline() const OVERRIDE {
274 return can_compose_inline_;
276 virtual gfx::Rect GetCaretBounds() const OVERRIDE {
277 return caret_bounds_;
279 virtual bool GetCompositionCharacterBounds(uint32 index,
280 gfx::Rect* rect) const OVERRIDE {
283 virtual bool HasCompositionText() const OVERRIDE {
284 CompositionText empty;
285 return composition_text_ != empty;
287 virtual bool GetTextRange(gfx::Range* range) const OVERRIDE {
288 *range = text_range_;
291 virtual bool GetCompositionTextRange(gfx::Range* range) const OVERRIDE {
294 virtual bool GetSelectionRange(gfx::Range* range) const OVERRIDE {
295 *range = selection_range_;
299 virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE {
302 virtual bool DeleteRange(const gfx::Range& range) OVERRIDE { return false; }
303 virtual bool GetTextFromRange(const gfx::Range& range,
304 base::string16* text) const OVERRIDE {
305 *text = surrounding_text_.substr(range.GetMin(), range.length());
308 virtual void OnInputMethodChanged() OVERRIDE {
309 ++on_input_method_changed_call_count_;
311 virtual bool ChangeTextDirectionAndLayoutAlignment(
312 base::i18n::TextDirection direction) OVERRIDE { return false; }
313 virtual void ExtendSelectionAndDelete(size_t before,
314 size_t after) OVERRIDE {}
315 virtual void EnsureCaretInRect(const gfx::Rect& rect) OVERRIDE {}
316 virtual void OnCandidateWindowShown() OVERRIDE {}
317 virtual void OnCandidateWindowUpdated() OVERRIDE {}
318 virtual void OnCandidateWindowHidden() OVERRIDE {}
320 bool HasNativeEvent() const {
321 return dispatched_key_event_.HasNativeEvent();
325 dispatched_key_event_ = ui::KeyEvent(ui::ET_UNKNOWN, ui::VKEY_UNKNOWN, 0,
328 composition_text_.Clear();
329 confirmed_text_.Clear();
330 inserted_text_.clear();
332 inserted_char_flags_ = 0;
333 on_input_method_changed_call_count_ = 0;
335 input_type_ = TEXT_INPUT_TYPE_NONE;
336 input_mode_ = TEXT_INPUT_MODE_DEFAULT;
337 can_compose_inline_ = true;
338 caret_bounds_ = gfx::Rect();
341 scoped_ptr<TestableInputMethodChromeOS> ime_;
343 // Copy of the dispatched key event.
344 ui::KeyEvent dispatched_key_event_;
346 // Variables for remembering the parameters that are passed to
347 // ui::TextInputClient functions.
348 CompositionText composition_text_;
349 CompositionText confirmed_text_;
350 base::string16 inserted_text_;
351 base::char16 inserted_char_;
352 unsigned int on_input_method_changed_call_count_;
353 int inserted_char_flags_;
355 // Variables that will be returned from the ui::TextInputClient functions.
356 TextInputType input_type_;
357 TextInputMode input_mode_;
358 bool can_compose_inline_;
359 gfx::Rect caret_bounds_;
360 gfx::Range text_range_;
361 gfx::Range selection_range_;
362 base::string16 surrounding_text_;
364 scoped_ptr<chromeos::MockIMEEngineHandler> mock_ime_engine_handler_;
365 scoped_ptr<chromeos::MockIMECandidateWindowHandler>
366 mock_ime_candidate_window_handler_;
368 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSTest);
371 // Tests public APIs in ui::InputMethod first.
373 TEST_F(InputMethodChromeOSTest, GetInputLocale) {
374 // ui::InputMethodChromeOS does not support the API.
376 EXPECT_EQ("", ime_->GetInputLocale());
379 TEST_F(InputMethodChromeOSTest, IsActive) {
381 // ui::InputMethodChromeOS always returns true.
382 EXPECT_TRUE(ime_->IsActive());
385 TEST_F(InputMethodChromeOSTest, GetInputTextType) {
387 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
388 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
389 ime_->OnTextInputTypeChanged(this);
390 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
391 input_type_ = TEXT_INPUT_TYPE_TEXT;
392 ime_->OnTextInputTypeChanged(this);
393 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT, ime_->GetTextInputType());
396 TEST_F(InputMethodChromeOSTest, CanComposeInline) {
398 EXPECT_TRUE(ime_->CanComposeInline());
399 can_compose_inline_ = false;
400 ime_->OnTextInputTypeChanged(this);
401 EXPECT_FALSE(ime_->CanComposeInline());
404 TEST_F(InputMethodChromeOSTest, GetTextInputClient) {
406 EXPECT_EQ(this, ime_->GetTextInputClient());
407 ime_->SetFocusedTextInputClient(NULL);
408 EXPECT_EQ(NULL, ime_->GetTextInputClient());
411 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedClient) {
413 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
414 ime_->SetFocusedTextInputClient(NULL);
415 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
416 ime_->OnTextInputTypeChanged(this);
417 // The OnTextInputTypeChanged() call above should be ignored since |this| is
418 // not the current focused client.
419 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
421 ime_->SetFocusedTextInputClient(this);
422 ime_->OnTextInputTypeChanged(this);
423 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
426 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow) {
428 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
430 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
431 ime_->OnTextInputTypeChanged(this);
432 // The OnTextInputTypeChanged() call above should be ignored since the top-
433 // level window which the ime_ is attached to is not currently focused.
434 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
437 ime_->OnTextInputTypeChanged(this);
438 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
441 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow2) {
442 ime_->Init(false); // the top-level is initially unfocused.
443 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
444 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
445 ime_->OnTextInputTypeChanged(this);
446 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
449 ime_->OnTextInputTypeChanged(this);
450 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
453 // Confirm that IBusClient::FocusIn is called on "connected" if input_type_ is
455 TEST_F(InputMethodChromeOSTest, FocusIn_Text) {
457 // A context shouldn't be created since the daemon is not running.
458 EXPECT_EQ(0U, on_input_method_changed_call_count_);
459 // Click a text input form.
460 input_type_ = TEXT_INPUT_TYPE_TEXT;
461 ime_->OnTextInputTypeChanged(this);
462 // Since a form has focus, IBusClient::FocusIn() should be called.
463 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
466 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
467 // ui::TextInputClient::OnInputMethodChanged() should be called when
468 // ui::InputMethodChromeOS connects/disconnects to/from ibus-daemon and the
469 // current text input type is not NONE.
470 EXPECT_EQ(1U, on_input_method_changed_call_count_);
473 // Confirm that InputMethodEngine::FocusIn is called on "connected" even if
474 // input_type_ is PASSWORD.
475 TEST_F(InputMethodChromeOSTest, FocusIn_Password) {
477 EXPECT_EQ(0U, on_input_method_changed_call_count_);
478 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
479 ime_->OnTextInputTypeChanged(this);
480 // InputMethodEngine::FocusIn() should be called even for password field.
481 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
482 EXPECT_EQ(1U, on_input_method_changed_call_count_);
485 // Confirm that IBusClient::FocusOut is called as expected.
486 TEST_F(InputMethodChromeOSTest, FocusOut_None) {
487 input_type_ = TEXT_INPUT_TYPE_TEXT;
489 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
490 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
491 input_type_ = TEXT_INPUT_TYPE_NONE;
492 ime_->OnTextInputTypeChanged(this);
493 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
494 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
497 // Confirm that IBusClient::FocusOut is called as expected.
498 TEST_F(InputMethodChromeOSTest, FocusOut_Password) {
499 input_type_ = TEXT_INPUT_TYPE_TEXT;
501 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
502 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
503 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
504 ime_->OnTextInputTypeChanged(this);
505 EXPECT_EQ(2, mock_ime_engine_handler_->focus_in_call_count());
506 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
509 // FocusIn/FocusOut scenario test
510 TEST_F(InputMethodChromeOSTest, Focus_Scenario) {
512 // Confirm that both FocusIn and FocusOut are NOT called.
513 EXPECT_EQ(0, mock_ime_engine_handler_->focus_in_call_count());
514 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
515 EXPECT_EQ(TEXT_INPUT_TYPE_NONE,
516 mock_ime_engine_handler_->last_text_input_context().type);
517 EXPECT_EQ(TEXT_INPUT_MODE_DEFAULT,
518 mock_ime_engine_handler_->last_text_input_context().mode);
520 input_type_ = TEXT_INPUT_TYPE_TEXT;
521 input_mode_ = TEXT_INPUT_MODE_LATIN;
522 ime_->OnTextInputTypeChanged(this);
523 // Confirm that only FocusIn is called, the TextInputType is TEXT and the
524 // TextInputMode is LATIN..
525 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
526 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
527 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT,
528 mock_ime_engine_handler_->last_text_input_context().type);
529 EXPECT_EQ(TEXT_INPUT_MODE_LATIN,
530 mock_ime_engine_handler_->last_text_input_context().mode);
532 input_mode_ = TEXT_INPUT_MODE_KANA;
533 ime_->OnTextInputTypeChanged(this);
534 // Confirm that both FocusIn and FocusOut are called for mode change.
535 EXPECT_EQ(2, mock_ime_engine_handler_->focus_in_call_count());
536 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
537 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT,
538 mock_ime_engine_handler_->last_text_input_context().type);
539 EXPECT_EQ(TEXT_INPUT_MODE_KANA,
540 mock_ime_engine_handler_->last_text_input_context().mode);
542 input_type_ = TEXT_INPUT_TYPE_URL;
543 ime_->OnTextInputTypeChanged(this);
544 // Confirm that both FocusIn and FocusOut are called and the TextInputType is
546 EXPECT_EQ(3, mock_ime_engine_handler_->focus_in_call_count());
547 EXPECT_EQ(2, mock_ime_engine_handler_->focus_out_call_count());
548 EXPECT_EQ(TEXT_INPUT_TYPE_URL,
549 mock_ime_engine_handler_->last_text_input_context().type);
550 EXPECT_EQ(TEXT_INPUT_MODE_KANA,
551 mock_ime_engine_handler_->last_text_input_context().mode);
552 // Confirm that FocusOut is called when set focus to NULL client.
553 ime_->SetFocusedTextInputClient(NULL);
554 EXPECT_EQ(3, mock_ime_engine_handler_->focus_in_call_count());
555 EXPECT_EQ(3, mock_ime_engine_handler_->focus_out_call_count());
556 // Confirm that FocusIn is called when set focus to this client.
557 ime_->SetFocusedTextInputClient(this);
558 EXPECT_EQ(4, mock_ime_engine_handler_->focus_in_call_count());
559 EXPECT_EQ(3, mock_ime_engine_handler_->focus_out_call_count());
562 // Test if the new |caret_bounds_| is correctly sent to ibus-daemon.
563 TEST_F(InputMethodChromeOSTest, OnCaretBoundsChanged) {
564 input_type_ = TEXT_INPUT_TYPE_TEXT;
568 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
569 caret_bounds_ = gfx::Rect(1, 2, 3, 4);
570 ime_->OnCaretBoundsChanged(this);
573 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
574 caret_bounds_ = gfx::Rect(0, 2, 3, 4);
575 ime_->OnCaretBoundsChanged(this);
578 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
579 caret_bounds_ = gfx::Rect(0, 2, 3, 4); // unchanged
580 ime_->OnCaretBoundsChanged(this);
581 // Current InputMethodChromeOS implementation performs the IPC
582 // regardless of the bounds are changed or not.
585 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
588 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_NoAttribute) {
589 const base::string16 kSampleAsciiText = UTF8ToUTF16("Sample Text");
590 const uint32 kCursorPos = 2UL;
592 chromeos::CompositionText chromeos_composition_text;
593 chromeos_composition_text.set_text(kSampleAsciiText);
595 CompositionText composition_text;
596 ime_->ExtractCompositionText(
597 chromeos_composition_text, kCursorPos, &composition_text);
598 EXPECT_EQ(kSampleAsciiText, composition_text.text);
599 // If there is no selection, |selection| represents cursor position.
600 EXPECT_EQ(kCursorPos, composition_text.selection.start());
601 EXPECT_EQ(kCursorPos, composition_text.selection.end());
602 // If there is no underline, |underlines| contains one underline and it is
603 // whole text underline.
604 ASSERT_EQ(1UL, composition_text.underlines.size());
605 EXPECT_EQ(0UL, composition_text.underlines[0].start_offset);
606 EXPECT_EQ(kSampleAsciiText.size(), composition_text.underlines[0].end_offset);
607 EXPECT_FALSE(composition_text.underlines[0].thick);
610 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_SingleUnderline) {
611 const uint32 kCursorPos = 2UL;
613 // Set up chromeos composition text with one underline attribute.
614 chromeos::CompositionText chromeos_composition_text;
615 chromeos_composition_text.set_text(kSampleText);
616 chromeos::CompositionText::UnderlineAttribute underline;
617 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_SINGLE;
618 underline.start_index = 1UL;
619 underline.end_index = 4UL;
620 chromeos_composition_text.mutable_underline_attributes()->push_back(
623 CompositionText composition_text;
624 ime_->ExtractCompositionText(
625 chromeos_composition_text, kCursorPos, &composition_text);
626 EXPECT_EQ(kSampleText, composition_text.text);
627 // If there is no selection, |selection| represents cursor position.
628 EXPECT_EQ(kCursorPos, composition_text.selection.start());
629 EXPECT_EQ(kCursorPos, composition_text.selection.end());
630 ASSERT_EQ(1UL, composition_text.underlines.size());
631 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
632 composition_text.underlines[0].start_offset);
633 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
634 composition_text.underlines[0].end_offset);
635 // Single underline represents as black thin line.
636 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
637 EXPECT_FALSE(composition_text.underlines[0].thick);
640 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_DoubleUnderline) {
641 const uint32 kCursorPos = 2UL;
643 // Set up chromeos composition text with one underline attribute.
644 chromeos::CompositionText chromeos_composition_text;
645 chromeos_composition_text.set_text(kSampleText);
646 chromeos::CompositionText::UnderlineAttribute underline;
647 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_DOUBLE;
648 underline.start_index = 1UL;
649 underline.end_index = 4UL;
650 chromeos_composition_text.mutable_underline_attributes()->push_back(
653 CompositionText composition_text;
654 ime_->ExtractCompositionText(
655 chromeos_composition_text, kCursorPos, &composition_text);
656 EXPECT_EQ(kSampleText, composition_text.text);
657 // If there is no selection, |selection| represents cursor position.
658 EXPECT_EQ(kCursorPos, composition_text.selection.start());
659 EXPECT_EQ(kCursorPos, composition_text.selection.end());
660 ASSERT_EQ(1UL, composition_text.underlines.size());
661 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
662 composition_text.underlines[0].start_offset);
663 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
664 composition_text.underlines[0].end_offset);
665 // Double underline represents as black thick line.
666 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
667 EXPECT_TRUE(composition_text.underlines[0].thick);
670 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_ErrorUnderline) {
671 const uint32 kCursorPos = 2UL;
673 // Set up chromeos composition text with one underline attribute.
674 chromeos::CompositionText chromeos_composition_text;
675 chromeos_composition_text.set_text(kSampleText);
676 chromeos::CompositionText::UnderlineAttribute underline;
677 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_ERROR;
678 underline.start_index = 1UL;
679 underline.end_index = 4UL;
680 chromeos_composition_text.mutable_underline_attributes()->push_back(
683 CompositionText composition_text;
684 ime_->ExtractCompositionText(
685 chromeos_composition_text, kCursorPos, &composition_text);
686 EXPECT_EQ(kSampleText, composition_text.text);
687 EXPECT_EQ(kCursorPos, composition_text.selection.start());
688 EXPECT_EQ(kCursorPos, composition_text.selection.end());
689 ASSERT_EQ(1UL, composition_text.underlines.size());
690 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
691 composition_text.underlines[0].start_offset);
692 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
693 composition_text.underlines[0].end_offset);
694 // Error underline represents as red thin line.
695 EXPECT_EQ(SK_ColorRED, composition_text.underlines[0].color);
696 EXPECT_FALSE(composition_text.underlines[0].thick);
699 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_Selection) {
700 const uint32 kCursorPos = 2UL;
702 // Set up chromeos composition text with one underline attribute.
703 chromeos::CompositionText chromeos_composition_text;
704 chromeos_composition_text.set_text(kSampleText);
705 chromeos_composition_text.set_selection_start(1UL);
706 chromeos_composition_text.set_selection_end(4UL);
708 CompositionText composition_text;
709 ime_->ExtractCompositionText(
710 chromeos_composition_text, kCursorPos, &composition_text);
711 EXPECT_EQ(kSampleText, composition_text.text);
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,
716 chromeos_composition_text.selection_start()),
717 composition_text.underlines[0].start_offset);
718 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
719 chromeos_composition_text.selection_end()),
720 composition_text.underlines[0].end_offset);
721 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
722 EXPECT_TRUE(composition_text.underlines[0].thick);
725 TEST_F(InputMethodChromeOSTest,
726 ExtractCompositionTextTest_SelectionStartWithCursor) {
727 const uint32 kCursorPos = 1UL;
729 // Set up chromeos composition text with one underline attribute.
730 chromeos::CompositionText chromeos_composition_text;
731 chromeos_composition_text.set_text(kSampleText);
732 chromeos_composition_text.set_selection_start(kCursorPos);
733 chromeos_composition_text.set_selection_end(4UL);
735 CompositionText composition_text;
736 ime_->ExtractCompositionText(
737 chromeos_composition_text, kCursorPos, &composition_text);
738 EXPECT_EQ(kSampleText, composition_text.text);
739 // If the cursor position is same as selection bounds, selection start
740 // position become opposit side of selection from cursor.
741 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
742 chromeos_composition_text.selection_end()),
743 composition_text.selection.start());
744 EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
745 composition_text.selection.end());
746 ASSERT_EQ(1UL, composition_text.underlines.size());
747 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
748 chromeos_composition_text.selection_start()),
749 composition_text.underlines[0].start_offset);
750 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
751 chromeos_composition_text.selection_end()),
752 composition_text.underlines[0].end_offset);
753 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
754 EXPECT_TRUE(composition_text.underlines[0].thick);
757 TEST_F(InputMethodChromeOSTest,
758 ExtractCompositionTextTest_SelectionEndWithCursor) {
759 const uint32 kCursorPos = 4UL;
761 // Set up chromeos composition text with one underline attribute.
762 chromeos::CompositionText chromeos_composition_text;
763 chromeos_composition_text.set_text(kSampleText);
764 chromeos_composition_text.set_selection_start(1UL);
765 chromeos_composition_text.set_selection_end(kCursorPos);
767 CompositionText composition_text;
768 ime_->ExtractCompositionText(
769 chromeos_composition_text, kCursorPos, &composition_text);
770 EXPECT_EQ(kSampleText, composition_text.text);
771 // If the cursor position is same as selection bounds, selection start
772 // position become opposit side of selection from cursor.
773 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
774 chromeos_composition_text.selection_start()),
775 composition_text.selection.start());
776 EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
777 composition_text.selection.end());
778 ASSERT_EQ(1UL, composition_text.underlines.size());
779 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
780 chromeos_composition_text.selection_start()),
781 composition_text.underlines[0].start_offset);
782 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
783 chromeos_composition_text.selection_end()),
784 composition_text.underlines[0].end_offset);
785 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
786 EXPECT_TRUE(composition_text.underlines[0].thick);
789 TEST_F(InputMethodChromeOSTest, SurroundingText_NoSelectionTest) {
791 // Click a text input form.
792 input_type_ = TEXT_INPUT_TYPE_TEXT;
793 ime_->OnTextInputTypeChanged(this);
795 // Set the TextInputClient behaviors.
796 surrounding_text_ = UTF8ToUTF16("abcdef");
797 text_range_ = gfx::Range(0, 6);
798 selection_range_ = gfx::Range(3, 3);
800 // Set the verifier for SetSurroundingText mock call.
801 SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 3, 3);
804 ime_->OnCaretBoundsChanged(this);
806 // Check the call count.
808 mock_ime_engine_handler_->set_surrounding_text_call_count());
809 EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
810 mock_ime_engine_handler_->last_set_surrounding_text());
812 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
814 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
817 TEST_F(InputMethodChromeOSTest, SurroundingText_SelectionTest) {
819 // Click a text input form.
820 input_type_ = TEXT_INPUT_TYPE_TEXT;
821 ime_->OnTextInputTypeChanged(this);
823 // Set the TextInputClient behaviors.
824 surrounding_text_ = UTF8ToUTF16("abcdef");
825 text_range_ = gfx::Range(0, 6);
826 selection_range_ = gfx::Range(2, 5);
828 // Set the verifier for SetSurroundingText mock call.
829 SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 2, 5);
831 ime_->OnCaretBoundsChanged(this);
833 // Check the call count.
835 mock_ime_engine_handler_->set_surrounding_text_call_count());
836 EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
837 mock_ime_engine_handler_->last_set_surrounding_text());
839 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
841 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
844 TEST_F(InputMethodChromeOSTest, SurroundingText_PartialText) {
846 // Click a text input form.
847 input_type_ = TEXT_INPUT_TYPE_TEXT;
848 ime_->OnTextInputTypeChanged(this);
850 // Set the TextInputClient behaviors.
851 surrounding_text_ = UTF8ToUTF16("abcdefghij");
852 text_range_ = gfx::Range(5, 10);
853 selection_range_ = gfx::Range(7, 9);
855 ime_->OnCaretBoundsChanged(this);
857 // Check the call count.
859 mock_ime_engine_handler_->set_surrounding_text_call_count());
860 // Set the verifier for SetSurroundingText mock call.
861 // Here (2, 4) is selection range in expected surrounding text coordinates.
863 mock_ime_engine_handler_->last_set_surrounding_text());
865 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
867 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
870 TEST_F(InputMethodChromeOSTest, SurroundingText_BecomeEmptyText) {
872 // Click a text input form.
873 input_type_ = TEXT_INPUT_TYPE_TEXT;
874 ime_->OnTextInputTypeChanged(this);
876 // Set the TextInputClient behaviors.
877 // If the surrounding text becomes empty, text_range become (0, 0) and
878 // selection range become invalid.
879 surrounding_text_ = UTF8ToUTF16("");
880 text_range_ = gfx::Range(0, 0);
881 selection_range_ = gfx::Range::InvalidRange();
883 ime_->OnCaretBoundsChanged(this);
885 // Check the call count.
887 mock_ime_engine_handler_->set_surrounding_text_call_count());
889 // Should not be called twice with same condition.
890 ime_->OnCaretBoundsChanged(this);
892 mock_ime_engine_handler_->set_surrounding_text_call_count());
895 class InputMethodChromeOSKeyEventTest : public InputMethodChromeOSTest {
897 InputMethodChromeOSKeyEventTest() {}
898 virtual ~InputMethodChromeOSKeyEventTest() {}
900 virtual void SetUp() OVERRIDE {
901 InputMethodChromeOSTest::SetUp();
905 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSKeyEventTest);
908 TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseTest) {
909 const int kFlags = ui::EF_SHIFT_DOWN;
910 ScopedXI2Event xevent;
911 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, kFlags);
912 const ui::KeyEvent event(xevent, true);
915 input_type_ = TEXT_INPUT_TYPE_TEXT;
916 ime_->OnTextInputTypeChanged(this);
917 ime_->DispatchKeyEvent(event);
919 // Check before state.
920 const ui::KeyEvent* key_event =
921 mock_ime_engine_handler_->last_processed_key_event();
922 EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count());
923 EXPECT_EQ(ui::VKEY_A, key_event->key_code());
924 EXPECT_EQ("KeyA", key_event->code());
925 EXPECT_EQ(kFlags, key_event->flags());
926 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
929 mock_ime_engine_handler_->last_passed_callback().Run(true);
932 EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
933 const ui::KeyEvent* stored_event =
934 ime_->process_key_event_post_ime_args().event;
935 EXPECT_TRUE(stored_event->HasNativeEvent());
936 EXPECT_TRUE(IsEqualXKeyEvent(*xevent, *(stored_event->native_event())));
937 EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
940 TEST_F(InputMethodChromeOSKeyEventTest, MultiKeyEventDelayResponseTest) {
942 input_type_ = TEXT_INPUT_TYPE_TEXT;
943 ime_->OnTextInputTypeChanged(this);
945 const int kFlags = ui::EF_SHIFT_DOWN;
946 ScopedXI2Event xevent;
947 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_B, kFlags);
948 const ui::KeyEvent event(xevent, true);
951 ime_->DispatchKeyEvent(event);
952 const ui::KeyEvent* key_event =
953 mock_ime_engine_handler_->last_processed_key_event();
954 EXPECT_EQ(ui::VKEY_B, key_event->key_code());
955 EXPECT_EQ("KeyB", key_event->code());
956 EXPECT_EQ(kFlags, key_event->flags());
958 KeyEventCallback first_callback =
959 mock_ime_engine_handler_->last_passed_callback();
961 // Do key event again.
962 ScopedXI2Event xevent2;
963 xevent2.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_C, kFlags);
964 const ui::KeyEvent event2(xevent2, true);
966 ime_->DispatchKeyEvent(event2);
967 const ui::KeyEvent* key_event2 =
968 mock_ime_engine_handler_->last_processed_key_event();
969 EXPECT_EQ(ui::VKEY_C, key_event2->key_code());
970 EXPECT_EQ("KeyC", key_event2->code());
971 EXPECT_EQ(kFlags, key_event2->flags());
973 // Check before state.
975 mock_ime_engine_handler_->process_key_event_call_count());
976 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
978 // Do callback for first key event.
979 first_callback.Run(true);
981 // Check the results for first key event.
982 EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
983 const ui::KeyEvent* stored_event =
984 ime_->process_key_event_post_ime_args().event;
985 EXPECT_TRUE(stored_event->HasNativeEvent());
986 EXPECT_TRUE(IsEqualXKeyEvent(*xevent, *(stored_event->native_event())));
987 EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
989 // Do callback for second key event.
990 mock_ime_engine_handler_->last_passed_callback().Run(false);
992 // Check the results for second key event.
993 EXPECT_EQ(2, ime_->process_key_event_post_ime_call_count());
994 stored_event = ime_->process_key_event_post_ime_args().event;
995 EXPECT_TRUE(stored_event->HasNativeEvent());
996 EXPECT_TRUE(IsEqualXKeyEvent(*xevent2, *(stored_event->native_event())));
997 EXPECT_FALSE(ime_->process_key_event_post_ime_args().handled);
1000 TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseResetTest) {
1001 ScopedXI2Event xevent;
1002 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_SHIFT_DOWN);
1003 const ui::KeyEvent event(xevent, true);
1006 input_type_ = TEXT_INPUT_TYPE_TEXT;
1007 ime_->OnTextInputTypeChanged(this);
1008 ime_->DispatchKeyEvent(event);
1010 // Check before state.
1011 EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count());
1012 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1014 ime_->ResetContext();
1017 mock_ime_engine_handler_->last_passed_callback().Run(true);
1019 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1021 // TODO(nona): Introduce ProcessKeyEventPostIME tests(crbug.com/156593).