Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / ui / base / ime / input_method_chromeos_unittest.cc
blob1db29e94af08ce3e5daa18867070eaac17c2e01c
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"
7 #include <X11/Xlib.h>
8 #undef Bool
9 #undef FocusIn
10 #undef FocusOut
11 #undef None
13 #include <cstring>
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/base/ime/text_input_focus_manager.h"
26 #include "ui/base/ui_base_switches_util.h"
27 #include "ui/events/event.h"
28 #include "ui/events/test/events_test_utils_x11.h"
29 #include "ui/gfx/geometry/rect.h"
31 using base::UTF8ToUTF16;
32 using base::UTF16ToUTF8;
34 namespace ui {
35 namespace {
37 const base::string16 kSampleText = base::UTF8ToUTF16(
38 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
40 typedef chromeos::IMEEngineHandlerInterface::KeyEventDoneCallback
41 KeyEventCallback;
43 uint32 GetOffsetInUTF16(
44 const base::string16& utf16_string, uint32 utf8_offset) {
45 DCHECK_LT(utf8_offset, utf16_string.size());
46 base::i18n::UTF16CharIterator char_iterator(&utf16_string);
47 for (size_t i = 0; i < utf8_offset; ++i)
48 char_iterator.Advance();
49 return char_iterator.array_pos();
52 bool IsEqualXKeyEvent(const XEvent& e1, const XEvent& e2) {
53 if ((e1.type == KeyPress && e2.type == KeyPress) ||
54 (e1.type == KeyRelease && e2.type == KeyRelease)) {
55 return !std::memcmp(&e1.xkey, &e2.xkey, sizeof(XKeyEvent));
57 return false;
60 enum KeyEventHandlerBehavior {
61 KEYEVENT_CONSUME,
62 KEYEVENT_NOT_CONSUME,
65 } // namespace
68 class TestableInputMethodChromeOS : public InputMethodChromeOS {
69 public:
70 explicit TestableInputMethodChromeOS(internal::InputMethodDelegate* delegate)
71 : InputMethodChromeOS(delegate),
72 process_key_event_post_ime_call_count_(0) {
75 struct ProcessKeyEventPostIMEArgs {
76 ProcessKeyEventPostIMEArgs() : event(NULL), handled(false) {}
77 const ui::KeyEvent* event;
78 bool handled;
81 // Overridden from InputMethodChromeOS:
82 virtual void ProcessKeyEventPostIME(const ui::KeyEvent& key_event,
83 bool handled) OVERRIDE {
84 process_key_event_post_ime_args_.event = &key_event;
85 process_key_event_post_ime_args_.handled = handled;
86 ++process_key_event_post_ime_call_count_;
89 void ResetCallCount() {
90 process_key_event_post_ime_call_count_ = 0;
93 const ProcessKeyEventPostIMEArgs& process_key_event_post_ime_args() const {
94 return process_key_event_post_ime_args_;
97 int process_key_event_post_ime_call_count() const {
98 return process_key_event_post_ime_call_count_;
101 // Change access rights for testing.
102 using InputMethodChromeOS::ExtractCompositionText;
103 using InputMethodChromeOS::ResetContext;
105 private:
106 ProcessKeyEventPostIMEArgs process_key_event_post_ime_args_;
107 int process_key_event_post_ime_call_count_;
110 class SynchronousKeyEventHandler {
111 public:
112 SynchronousKeyEventHandler(uint32 expected_keyval,
113 uint32 expected_keycode,
114 uint32 expected_state,
115 KeyEventHandlerBehavior behavior)
116 : expected_keyval_(expected_keyval),
117 expected_keycode_(expected_keycode),
118 expected_state_(expected_state),
119 behavior_(behavior) {}
121 virtual ~SynchronousKeyEventHandler() {}
123 void Run(uint32 keyval,
124 uint32 keycode,
125 uint32 state,
126 const KeyEventCallback& callback) {
127 EXPECT_EQ(expected_keyval_, keyval);
128 EXPECT_EQ(expected_keycode_, keycode);
129 EXPECT_EQ(expected_state_, state);
130 callback.Run(behavior_ == KEYEVENT_CONSUME);
133 private:
134 const uint32 expected_keyval_;
135 const uint32 expected_keycode_;
136 const uint32 expected_state_;
137 const KeyEventHandlerBehavior behavior_;
139 DISALLOW_COPY_AND_ASSIGN(SynchronousKeyEventHandler);
142 class AsynchronousKeyEventHandler {
143 public:
144 AsynchronousKeyEventHandler(uint32 expected_keyval,
145 uint32 expected_keycode,
146 uint32 expected_state)
147 : expected_keyval_(expected_keyval),
148 expected_keycode_(expected_keycode),
149 expected_state_(expected_state) {}
151 virtual ~AsynchronousKeyEventHandler() {}
153 void Run(uint32 keyval,
154 uint32 keycode,
155 uint32 state,
156 const KeyEventCallback& callback) {
157 EXPECT_EQ(expected_keyval_, keyval);
158 EXPECT_EQ(expected_keycode_, keycode);
159 EXPECT_EQ(expected_state_, state);
160 callback_ = callback;
163 void RunCallback(KeyEventHandlerBehavior behavior) {
164 callback_.Run(behavior == KEYEVENT_CONSUME);
167 private:
168 const uint32 expected_keyval_;
169 const uint32 expected_keycode_;
170 const uint32 expected_state_;
171 KeyEventCallback callback_;
173 DISALLOW_COPY_AND_ASSIGN(AsynchronousKeyEventHandler);
176 class SetSurroundingTextVerifier {
177 public:
178 SetSurroundingTextVerifier(const std::string& expected_surrounding_text,
179 uint32 expected_cursor_position,
180 uint32 expected_anchor_position)
181 : expected_surrounding_text_(expected_surrounding_text),
182 expected_cursor_position_(expected_cursor_position),
183 expected_anchor_position_(expected_anchor_position) {}
185 void Verify(const std::string& text,
186 uint32 cursor_pos,
187 uint32 anchor_pos) {
188 EXPECT_EQ(expected_surrounding_text_, text);
189 EXPECT_EQ(expected_cursor_position_, cursor_pos);
190 EXPECT_EQ(expected_anchor_position_, anchor_pos);
193 private:
194 const std::string expected_surrounding_text_;
195 const uint32 expected_cursor_position_;
196 const uint32 expected_anchor_position_;
198 DISALLOW_COPY_AND_ASSIGN(SetSurroundingTextVerifier);
201 class InputMethodChromeOSTest : public internal::InputMethodDelegate,
202 public testing::Test,
203 public TextInputClient {
204 public:
205 InputMethodChromeOSTest()
206 : dispatched_key_event_(ui::ET_UNKNOWN, ui::VKEY_UNKNOWN, ui::EF_NONE) {
207 ResetFlags();
210 virtual ~InputMethodChromeOSTest() {
213 virtual void SetUp() OVERRIDE {
214 chromeos::IMEBridge::Initialize();
216 mock_ime_engine_handler_.reset(
217 new chromeos::MockIMEEngineHandler());
218 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(
219 mock_ime_engine_handler_.get());
221 mock_ime_candidate_window_handler_.reset(
222 new chromeos::MockIMECandidateWindowHandler());
223 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(
224 mock_ime_candidate_window_handler_.get());
226 ime_.reset(new TestableInputMethodChromeOS(this));
227 if (switches::IsTextInputFocusManagerEnabled())
228 TextInputFocusManager::GetInstance()->FocusTextInputClient(this);
229 else
230 ime_->SetFocusedTextInputClient(this);
233 virtual void TearDown() OVERRIDE {
234 if (ime_.get()) {
235 if (switches::IsTextInputFocusManagerEnabled())
236 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
237 else
238 ime_->SetFocusedTextInputClient(NULL);
240 ime_.reset();
241 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(NULL);
242 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(NULL);
243 mock_ime_engine_handler_.reset();
244 mock_ime_candidate_window_handler_.reset();
245 chromeos::IMEBridge::Shutdown();
248 // Overridden from ui::internal::InputMethodDelegate:
249 virtual bool DispatchKeyEventPostIME(const ui::KeyEvent& event) OVERRIDE {
250 dispatched_key_event_ = event;
251 return false;
254 // Overridden from ui::TextInputClient:
255 virtual void SetCompositionText(
256 const CompositionText& composition) OVERRIDE {
257 composition_text_ = composition;
259 virtual void ConfirmCompositionText() OVERRIDE {
260 confirmed_text_ = composition_text_;
261 composition_text_.Clear();
263 virtual void ClearCompositionText() OVERRIDE {
264 composition_text_.Clear();
266 virtual void InsertText(const base::string16& text) OVERRIDE {
267 inserted_text_ = text;
269 virtual void InsertChar(base::char16 ch, int flags) OVERRIDE {
270 inserted_char_ = ch;
271 inserted_char_flags_ = flags;
273 virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE {
274 return static_cast<gfx::NativeWindow>(NULL);
276 virtual TextInputType GetTextInputType() const OVERRIDE {
277 return input_type_;
279 virtual TextInputMode GetTextInputMode() const OVERRIDE {
280 return input_mode_;
282 virtual bool CanComposeInline() const OVERRIDE {
283 return can_compose_inline_;
285 virtual gfx::Rect GetCaretBounds() const OVERRIDE {
286 return caret_bounds_;
288 virtual bool GetCompositionCharacterBounds(uint32 index,
289 gfx::Rect* rect) const OVERRIDE {
290 return false;
292 virtual bool HasCompositionText() const OVERRIDE {
293 CompositionText empty;
294 return composition_text_ != empty;
296 virtual bool GetTextRange(gfx::Range* range) const OVERRIDE {
297 *range = text_range_;
298 return true;
300 virtual bool GetCompositionTextRange(gfx::Range* range) const OVERRIDE {
301 return false;
303 virtual bool GetSelectionRange(gfx::Range* range) const OVERRIDE {
304 *range = selection_range_;
305 return true;
308 virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE {
309 return false;
311 virtual bool DeleteRange(const gfx::Range& range) OVERRIDE { return false; }
312 virtual bool GetTextFromRange(const gfx::Range& range,
313 base::string16* text) const OVERRIDE {
314 *text = surrounding_text_.substr(range.GetMin(), range.length());
315 return true;
317 virtual void OnInputMethodChanged() OVERRIDE {
318 ++on_input_method_changed_call_count_;
320 virtual bool ChangeTextDirectionAndLayoutAlignment(
321 base::i18n::TextDirection direction) OVERRIDE { return false; }
322 virtual void ExtendSelectionAndDelete(size_t before,
323 size_t after) OVERRIDE {}
324 virtual void EnsureCaretInRect(const gfx::Rect& rect) OVERRIDE {}
325 virtual void OnCandidateWindowShown() OVERRIDE {}
326 virtual void OnCandidateWindowUpdated() OVERRIDE {}
327 virtual void OnCandidateWindowHidden() OVERRIDE {}
328 virtual bool IsEditingCommandEnabled(int command_id) OVERRIDE {
329 return false;
331 virtual void ExecuteEditingCommand(int command_id) OVERRIDE {}
333 bool HasNativeEvent() const {
334 return dispatched_key_event_.HasNativeEvent();
337 void ResetFlags() {
338 dispatched_key_event_ = ui::KeyEvent(ui::ET_UNKNOWN, ui::VKEY_UNKNOWN,
339 ui::EF_NONE);
341 composition_text_.Clear();
342 confirmed_text_.Clear();
343 inserted_text_.clear();
344 inserted_char_ = 0;
345 inserted_char_flags_ = 0;
346 on_input_method_changed_call_count_ = 0;
348 input_type_ = TEXT_INPUT_TYPE_NONE;
349 input_mode_ = TEXT_INPUT_MODE_DEFAULT;
350 can_compose_inline_ = true;
351 caret_bounds_ = gfx::Rect();
354 scoped_ptr<TestableInputMethodChromeOS> ime_;
356 // Copy of the dispatched key event.
357 ui::KeyEvent dispatched_key_event_;
359 // Variables for remembering the parameters that are passed to
360 // ui::TextInputClient functions.
361 CompositionText composition_text_;
362 CompositionText confirmed_text_;
363 base::string16 inserted_text_;
364 base::char16 inserted_char_;
365 unsigned int on_input_method_changed_call_count_;
366 int inserted_char_flags_;
368 // Variables that will be returned from the ui::TextInputClient functions.
369 TextInputType input_type_;
370 TextInputMode input_mode_;
371 bool can_compose_inline_;
372 gfx::Rect caret_bounds_;
373 gfx::Range text_range_;
374 gfx::Range selection_range_;
375 base::string16 surrounding_text_;
377 scoped_ptr<chromeos::MockIMEEngineHandler> mock_ime_engine_handler_;
378 scoped_ptr<chromeos::MockIMECandidateWindowHandler>
379 mock_ime_candidate_window_handler_;
381 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSTest);
384 // Tests public APIs in ui::InputMethod first.
386 TEST_F(InputMethodChromeOSTest, GetInputLocale) {
387 // ui::InputMethodChromeOS does not support the API.
388 ime_->Init(true);
389 EXPECT_EQ("", ime_->GetInputLocale());
392 TEST_F(InputMethodChromeOSTest, IsActive) {
393 ime_->Init(true);
394 // ui::InputMethodChromeOS always returns true.
395 EXPECT_TRUE(ime_->IsActive());
398 TEST_F(InputMethodChromeOSTest, GetInputTextType) {
399 ime_->Init(true);
400 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
401 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
402 ime_->OnTextInputTypeChanged(this);
403 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
404 input_type_ = TEXT_INPUT_TYPE_TEXT;
405 ime_->OnTextInputTypeChanged(this);
406 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT, ime_->GetTextInputType());
409 TEST_F(InputMethodChromeOSTest, CanComposeInline) {
410 ime_->Init(true);
411 EXPECT_TRUE(ime_->CanComposeInline());
412 can_compose_inline_ = false;
413 ime_->OnTextInputTypeChanged(this);
414 EXPECT_FALSE(ime_->CanComposeInline());
417 TEST_F(InputMethodChromeOSTest, GetTextInputClient) {
418 ime_->Init(true);
419 EXPECT_EQ(this, ime_->GetTextInputClient());
420 if (switches::IsTextInputFocusManagerEnabled())
421 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
422 else
423 ime_->SetFocusedTextInputClient(NULL);
424 EXPECT_EQ(NULL, ime_->GetTextInputClient());
427 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedClient) {
428 ime_->Init(true);
429 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
430 if (switches::IsTextInputFocusManagerEnabled())
431 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
432 else
433 ime_->SetFocusedTextInputClient(NULL);
434 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
435 ime_->OnTextInputTypeChanged(this);
436 // The OnTextInputTypeChanged() call above should be ignored since |this| is
437 // not the current focused client.
438 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
440 if (switches::IsTextInputFocusManagerEnabled())
441 TextInputFocusManager::GetInstance()->FocusTextInputClient(this);
442 else
443 ime_->SetFocusedTextInputClient(this);
444 ime_->OnTextInputTypeChanged(this);
445 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
448 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow) {
449 ime_->Init(true);
450 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
451 if (switches::IsTextInputFocusManagerEnabled())
452 TextInputFocusManager::GetInstance()->BlurTextInputClient(this);
453 else
454 ime_->OnBlur();
455 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
456 ime_->OnTextInputTypeChanged(this);
457 // The OnTextInputTypeChanged() call above should be ignored since the top-
458 // level window which the ime_ is attached to is not currently focused.
459 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
461 if (switches::IsTextInputFocusManagerEnabled())
462 TextInputFocusManager::GetInstance()->FocusTextInputClient(this);
463 else
464 ime_->OnFocus();
465 ime_->OnTextInputTypeChanged(this);
466 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
469 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow2) {
470 // We no longer support the case that |ime_->Init(false)| because no one
471 // actually uses it.
472 if (switches::IsTextInputFocusManagerEnabled())
473 return;
475 ime_->Init(false); // the top-level is initially unfocused.
476 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
477 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
478 ime_->OnTextInputTypeChanged(this);
479 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
481 ime_->OnFocus();
482 ime_->OnTextInputTypeChanged(this);
483 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
486 // Confirm that IBusClient::FocusIn is called on "connected" if input_type_ is
487 // TEXT.
488 TEST_F(InputMethodChromeOSTest, FocusIn_Text) {
489 ime_->Init(true);
490 // A context shouldn't be created since the daemon is not running.
491 EXPECT_EQ(0U, on_input_method_changed_call_count_);
492 // Click a text input form.
493 input_type_ = TEXT_INPUT_TYPE_TEXT;
494 ime_->OnTextInputTypeChanged(this);
495 // Since a form has focus, IBusClient::FocusIn() should be called.
496 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
497 EXPECT_EQ(
499 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
500 // ui::TextInputClient::OnInputMethodChanged() should be called when
501 // ui::InputMethodChromeOS connects/disconnects to/from ibus-daemon and the
502 // current text input type is not NONE.
503 EXPECT_EQ(1U, on_input_method_changed_call_count_);
506 // Confirm that InputMethodEngine::FocusIn is called on "connected" even if
507 // input_type_ is PASSWORD.
508 TEST_F(InputMethodChromeOSTest, FocusIn_Password) {
509 ime_->Init(true);
510 EXPECT_EQ(0U, on_input_method_changed_call_count_);
511 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
512 ime_->OnTextInputTypeChanged(this);
513 // InputMethodEngine::FocusIn() should be called even for password field.
514 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
515 EXPECT_EQ(1U, on_input_method_changed_call_count_);
518 // Confirm that IBusClient::FocusOut is called as expected.
519 TEST_F(InputMethodChromeOSTest, FocusOut_None) {
520 input_type_ = TEXT_INPUT_TYPE_TEXT;
521 ime_->Init(true);
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 input_type_ = TEXT_INPUT_TYPE_NONE;
525 ime_->OnTextInputTypeChanged(this);
526 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
527 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
530 // Confirm that IBusClient::FocusOut is called as expected.
531 TEST_F(InputMethodChromeOSTest, FocusOut_Password) {
532 input_type_ = TEXT_INPUT_TYPE_TEXT;
533 ime_->Init(true);
534 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
535 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
536 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
537 ime_->OnTextInputTypeChanged(this);
538 EXPECT_EQ(2, mock_ime_engine_handler_->focus_in_call_count());
539 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
542 // FocusIn/FocusOut scenario test
543 TEST_F(InputMethodChromeOSTest, Focus_Scenario) {
544 ime_->Init(true);
545 // Confirm that both FocusIn and FocusOut are NOT called.
546 EXPECT_EQ(0, mock_ime_engine_handler_->focus_in_call_count());
547 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
548 EXPECT_EQ(TEXT_INPUT_TYPE_NONE,
549 mock_ime_engine_handler_->last_text_input_context().type);
550 EXPECT_EQ(TEXT_INPUT_MODE_DEFAULT,
551 mock_ime_engine_handler_->last_text_input_context().mode);
553 input_type_ = TEXT_INPUT_TYPE_TEXT;
554 input_mode_ = TEXT_INPUT_MODE_LATIN;
555 ime_->OnTextInputTypeChanged(this);
556 // Confirm that only FocusIn is called, the TextInputType is TEXT and the
557 // TextInputMode is LATIN..
558 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
559 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
560 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT,
561 mock_ime_engine_handler_->last_text_input_context().type);
562 EXPECT_EQ(TEXT_INPUT_MODE_LATIN,
563 mock_ime_engine_handler_->last_text_input_context().mode);
565 input_mode_ = TEXT_INPUT_MODE_KANA;
566 ime_->OnTextInputTypeChanged(this);
567 // Confirm that both FocusIn and FocusOut are called for mode change.
568 EXPECT_EQ(2, mock_ime_engine_handler_->focus_in_call_count());
569 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
570 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT,
571 mock_ime_engine_handler_->last_text_input_context().type);
572 EXPECT_EQ(TEXT_INPUT_MODE_KANA,
573 mock_ime_engine_handler_->last_text_input_context().mode);
575 input_type_ = TEXT_INPUT_TYPE_URL;
576 ime_->OnTextInputTypeChanged(this);
577 // Confirm that both FocusIn and FocusOut are called and the TextInputType is
578 // changed to URL.
579 EXPECT_EQ(3, mock_ime_engine_handler_->focus_in_call_count());
580 EXPECT_EQ(2, mock_ime_engine_handler_->focus_out_call_count());
581 EXPECT_EQ(TEXT_INPUT_TYPE_URL,
582 mock_ime_engine_handler_->last_text_input_context().type);
583 EXPECT_EQ(TEXT_INPUT_MODE_KANA,
584 mock_ime_engine_handler_->last_text_input_context().mode);
586 // When IsTextInputFocusManagerEnabled, InputMethod::SetFocusedTextInputClient
587 // is not supported and it's no-op.
588 if (switches::IsTextInputFocusManagerEnabled())
589 return;
590 // Confirm that FocusOut is called when set focus to NULL client.
591 ime_->SetFocusedTextInputClient(NULL);
592 EXPECT_EQ(3, mock_ime_engine_handler_->focus_in_call_count());
593 EXPECT_EQ(3, mock_ime_engine_handler_->focus_out_call_count());
594 // Confirm that FocusIn is called when set focus to this client.
595 ime_->SetFocusedTextInputClient(this);
596 EXPECT_EQ(4, mock_ime_engine_handler_->focus_in_call_count());
597 EXPECT_EQ(3, mock_ime_engine_handler_->focus_out_call_count());
600 // Test if the new |caret_bounds_| is correctly sent to ibus-daemon.
601 TEST_F(InputMethodChromeOSTest, OnCaretBoundsChanged) {
602 input_type_ = TEXT_INPUT_TYPE_TEXT;
603 ime_->Init(true);
604 EXPECT_EQ(
606 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
607 caret_bounds_ = gfx::Rect(1, 2, 3, 4);
608 ime_->OnCaretBoundsChanged(this);
609 EXPECT_EQ(
611 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
612 caret_bounds_ = gfx::Rect(0, 2, 3, 4);
613 ime_->OnCaretBoundsChanged(this);
614 EXPECT_EQ(
616 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
617 caret_bounds_ = gfx::Rect(0, 2, 3, 4); // unchanged
618 ime_->OnCaretBoundsChanged(this);
619 // Current InputMethodChromeOS implementation performs the IPC
620 // regardless of the bounds are changed or not.
621 EXPECT_EQ(
623 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
626 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_NoAttribute) {
627 const base::string16 kSampleAsciiText = UTF8ToUTF16("Sample Text");
628 const uint32 kCursorPos = 2UL;
630 chromeos::CompositionText chromeos_composition_text;
631 chromeos_composition_text.set_text(kSampleAsciiText);
633 CompositionText composition_text;
634 ime_->ExtractCompositionText(
635 chromeos_composition_text, kCursorPos, &composition_text);
636 EXPECT_EQ(kSampleAsciiText, composition_text.text);
637 // If there is no selection, |selection| represents cursor position.
638 EXPECT_EQ(kCursorPos, composition_text.selection.start());
639 EXPECT_EQ(kCursorPos, composition_text.selection.end());
640 // If there is no underline, |underlines| contains one underline and it is
641 // whole text underline.
642 ASSERT_EQ(1UL, composition_text.underlines.size());
643 EXPECT_EQ(0UL, composition_text.underlines[0].start_offset);
644 EXPECT_EQ(kSampleAsciiText.size(), composition_text.underlines[0].end_offset);
645 EXPECT_FALSE(composition_text.underlines[0].thick);
648 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_SingleUnderline) {
649 const uint32 kCursorPos = 2UL;
651 // Set up chromeos composition text with one underline attribute.
652 chromeos::CompositionText chromeos_composition_text;
653 chromeos_composition_text.set_text(kSampleText);
654 chromeos::CompositionText::UnderlineAttribute underline;
655 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_SINGLE;
656 underline.start_index = 1UL;
657 underline.end_index = 4UL;
658 chromeos_composition_text.mutable_underline_attributes()->push_back(
659 underline);
661 CompositionText composition_text;
662 ime_->ExtractCompositionText(
663 chromeos_composition_text, kCursorPos, &composition_text);
664 EXPECT_EQ(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 // Single underline represents as black thin line.
674 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
675 EXPECT_FALSE(composition_text.underlines[0].thick);
676 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
677 composition_text.underlines[0].background_color);
680 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_DoubleUnderline) {
681 const uint32 kCursorPos = 2UL;
683 // Set up chromeos composition text with one underline attribute.
684 chromeos::CompositionText chromeos_composition_text;
685 chromeos_composition_text.set_text(kSampleText);
686 chromeos::CompositionText::UnderlineAttribute underline;
687 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_DOUBLE;
688 underline.start_index = 1UL;
689 underline.end_index = 4UL;
690 chromeos_composition_text.mutable_underline_attributes()->push_back(
691 underline);
693 CompositionText composition_text;
694 ime_->ExtractCompositionText(
695 chromeos_composition_text, kCursorPos, &composition_text);
696 EXPECT_EQ(kSampleText, composition_text.text);
697 // If there is no selection, |selection| represents cursor position.
698 EXPECT_EQ(kCursorPos, composition_text.selection.start());
699 EXPECT_EQ(kCursorPos, composition_text.selection.end());
700 ASSERT_EQ(1UL, composition_text.underlines.size());
701 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
702 composition_text.underlines[0].start_offset);
703 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
704 composition_text.underlines[0].end_offset);
705 // Double underline represents as black thick line.
706 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
707 EXPECT_TRUE(composition_text.underlines[0].thick);
708 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
709 composition_text.underlines[0].background_color);
712 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_ErrorUnderline) {
713 const uint32 kCursorPos = 2UL;
715 // Set up chromeos composition text with one underline attribute.
716 chromeos::CompositionText chromeos_composition_text;
717 chromeos_composition_text.set_text(kSampleText);
718 chromeos::CompositionText::UnderlineAttribute underline;
719 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_ERROR;
720 underline.start_index = 1UL;
721 underline.end_index = 4UL;
722 chromeos_composition_text.mutable_underline_attributes()->push_back(
723 underline);
725 CompositionText composition_text;
726 ime_->ExtractCompositionText(
727 chromeos_composition_text, kCursorPos, &composition_text);
728 EXPECT_EQ(kSampleText, composition_text.text);
729 EXPECT_EQ(kCursorPos, composition_text.selection.start());
730 EXPECT_EQ(kCursorPos, composition_text.selection.end());
731 ASSERT_EQ(1UL, composition_text.underlines.size());
732 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
733 composition_text.underlines[0].start_offset);
734 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
735 composition_text.underlines[0].end_offset);
736 // Error underline represents as red thin line.
737 EXPECT_EQ(SK_ColorRED, composition_text.underlines[0].color);
738 EXPECT_FALSE(composition_text.underlines[0].thick);
741 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_Selection) {
742 const uint32 kCursorPos = 2UL;
744 // Set up chromeos composition text with one underline attribute.
745 chromeos::CompositionText chromeos_composition_text;
746 chromeos_composition_text.set_text(kSampleText);
747 chromeos_composition_text.set_selection_start(1UL);
748 chromeos_composition_text.set_selection_end(4UL);
750 CompositionText composition_text;
751 ime_->ExtractCompositionText(
752 chromeos_composition_text, kCursorPos, &composition_text);
753 EXPECT_EQ(kSampleText, composition_text.text);
754 EXPECT_EQ(kCursorPos, composition_text.selection.start());
755 EXPECT_EQ(kCursorPos, composition_text.selection.end());
756 ASSERT_EQ(1UL, composition_text.underlines.size());
757 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
758 chromeos_composition_text.selection_start()),
759 composition_text.underlines[0].start_offset);
760 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
761 chromeos_composition_text.selection_end()),
762 composition_text.underlines[0].end_offset);
763 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
764 EXPECT_TRUE(composition_text.underlines[0].thick);
765 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
766 composition_text.underlines[0].background_color);
769 TEST_F(InputMethodChromeOSTest,
770 ExtractCompositionTextTest_SelectionStartWithCursor) {
771 const uint32 kCursorPos = 1UL;
773 // Set up chromeos composition text with one underline attribute.
774 chromeos::CompositionText chromeos_composition_text;
775 chromeos_composition_text.set_text(kSampleText);
776 chromeos_composition_text.set_selection_start(kCursorPos);
777 chromeos_composition_text.set_selection_end(4UL);
779 CompositionText composition_text;
780 ime_->ExtractCompositionText(
781 chromeos_composition_text, kCursorPos, &composition_text);
782 EXPECT_EQ(kSampleText, composition_text.text);
783 // If the cursor position is same as selection bounds, selection start
784 // position become opposit side of selection from cursor.
785 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
786 chromeos_composition_text.selection_end()),
787 composition_text.selection.start());
788 EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
789 composition_text.selection.end());
790 ASSERT_EQ(1UL, composition_text.underlines.size());
791 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
792 chromeos_composition_text.selection_start()),
793 composition_text.underlines[0].start_offset);
794 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
795 chromeos_composition_text.selection_end()),
796 composition_text.underlines[0].end_offset);
797 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
798 EXPECT_TRUE(composition_text.underlines[0].thick);
799 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
800 composition_text.underlines[0].background_color);
803 TEST_F(InputMethodChromeOSTest,
804 ExtractCompositionTextTest_SelectionEndWithCursor) {
805 const uint32 kCursorPos = 4UL;
807 // Set up chromeos composition text with one underline attribute.
808 chromeos::CompositionText chromeos_composition_text;
809 chromeos_composition_text.set_text(kSampleText);
810 chromeos_composition_text.set_selection_start(1UL);
811 chromeos_composition_text.set_selection_end(kCursorPos);
813 CompositionText composition_text;
814 ime_->ExtractCompositionText(
815 chromeos_composition_text, kCursorPos, &composition_text);
816 EXPECT_EQ(kSampleText, composition_text.text);
817 // If the cursor position is same as selection bounds, selection start
818 // position become opposit side of selection from cursor.
819 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
820 chromeos_composition_text.selection_start()),
821 composition_text.selection.start());
822 EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
823 composition_text.selection.end());
824 ASSERT_EQ(1UL, composition_text.underlines.size());
825 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
826 chromeos_composition_text.selection_start()),
827 composition_text.underlines[0].start_offset);
828 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
829 chromeos_composition_text.selection_end()),
830 composition_text.underlines[0].end_offset);
831 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
832 EXPECT_TRUE(composition_text.underlines[0].thick);
833 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
834 composition_text.underlines[0].background_color);
837 TEST_F(InputMethodChromeOSTest, SurroundingText_NoSelectionTest) {
838 ime_->Init(true);
839 // Click a text input form.
840 input_type_ = TEXT_INPUT_TYPE_TEXT;
841 ime_->OnTextInputTypeChanged(this);
843 // Set the TextInputClient behaviors.
844 surrounding_text_ = UTF8ToUTF16("abcdef");
845 text_range_ = gfx::Range(0, 6);
846 selection_range_ = gfx::Range(3, 3);
848 // Set the verifier for SetSurroundingText mock call.
849 SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 3, 3);
852 ime_->OnCaretBoundsChanged(this);
854 // Check the call count.
855 EXPECT_EQ(1,
856 mock_ime_engine_handler_->set_surrounding_text_call_count());
857 EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
858 mock_ime_engine_handler_->last_set_surrounding_text());
859 EXPECT_EQ(3U,
860 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
861 EXPECT_EQ(3U,
862 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
865 TEST_F(InputMethodChromeOSTest, SurroundingText_SelectionTest) {
866 ime_->Init(true);
867 // Click a text input form.
868 input_type_ = TEXT_INPUT_TYPE_TEXT;
869 ime_->OnTextInputTypeChanged(this);
871 // Set the TextInputClient behaviors.
872 surrounding_text_ = UTF8ToUTF16("abcdef");
873 text_range_ = gfx::Range(0, 6);
874 selection_range_ = gfx::Range(2, 5);
876 // Set the verifier for SetSurroundingText mock call.
877 SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 2, 5);
879 ime_->OnCaretBoundsChanged(this);
881 // Check the call count.
882 EXPECT_EQ(1,
883 mock_ime_engine_handler_->set_surrounding_text_call_count());
884 EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
885 mock_ime_engine_handler_->last_set_surrounding_text());
886 EXPECT_EQ(2U,
887 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
888 EXPECT_EQ(5U,
889 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
892 TEST_F(InputMethodChromeOSTest, SurroundingText_PartialText) {
893 ime_->Init(true);
894 // Click a text input form.
895 input_type_ = TEXT_INPUT_TYPE_TEXT;
896 ime_->OnTextInputTypeChanged(this);
898 // Set the TextInputClient behaviors.
899 surrounding_text_ = UTF8ToUTF16("abcdefghij");
900 text_range_ = gfx::Range(5, 10);
901 selection_range_ = gfx::Range(7, 9);
903 ime_->OnCaretBoundsChanged(this);
905 // Check the call count.
906 EXPECT_EQ(1,
907 mock_ime_engine_handler_->set_surrounding_text_call_count());
908 // Set the verifier for SetSurroundingText mock call.
909 // Here (2, 4) is selection range in expected surrounding text coordinates.
910 EXPECT_EQ("fghij",
911 mock_ime_engine_handler_->last_set_surrounding_text());
912 EXPECT_EQ(2U,
913 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
914 EXPECT_EQ(4U,
915 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
918 TEST_F(InputMethodChromeOSTest, SurroundingText_BecomeEmptyText) {
919 ime_->Init(true);
920 // Click a text input form.
921 input_type_ = TEXT_INPUT_TYPE_TEXT;
922 ime_->OnTextInputTypeChanged(this);
924 // Set the TextInputClient behaviors.
925 // If the surrounding text becomes empty, text_range become (0, 0) and
926 // selection range become invalid.
927 surrounding_text_ = UTF8ToUTF16("");
928 text_range_ = gfx::Range(0, 0);
929 selection_range_ = gfx::Range::InvalidRange();
931 ime_->OnCaretBoundsChanged(this);
933 // Check the call count.
934 EXPECT_EQ(0,
935 mock_ime_engine_handler_->set_surrounding_text_call_count());
937 // Should not be called twice with same condition.
938 ime_->OnCaretBoundsChanged(this);
939 EXPECT_EQ(0,
940 mock_ime_engine_handler_->set_surrounding_text_call_count());
943 class InputMethodChromeOSKeyEventTest : public InputMethodChromeOSTest {
944 public:
945 InputMethodChromeOSKeyEventTest() {}
946 virtual ~InputMethodChromeOSKeyEventTest() {}
948 virtual void SetUp() OVERRIDE {
949 InputMethodChromeOSTest::SetUp();
950 ime_->Init(true);
953 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSKeyEventTest);
956 TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseTest) {
957 const int kFlags = ui::EF_SHIFT_DOWN;
958 ScopedXI2Event xevent;
959 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, kFlags);
960 const ui::KeyEvent event(xevent);
962 // Do key event.
963 input_type_ = TEXT_INPUT_TYPE_TEXT;
964 ime_->OnTextInputTypeChanged(this);
965 ime_->DispatchKeyEvent(event);
967 // Check before state.
968 const ui::KeyEvent* key_event =
969 mock_ime_engine_handler_->last_processed_key_event();
970 EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count());
971 EXPECT_EQ(ui::VKEY_A, key_event->key_code());
972 EXPECT_EQ("KeyA", key_event->code());
973 EXPECT_EQ(kFlags, key_event->flags());
974 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
976 // Do callback.
977 mock_ime_engine_handler_->last_passed_callback().Run(true);
979 // Check the results
980 EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
981 const ui::KeyEvent* stored_event =
982 ime_->process_key_event_post_ime_args().event;
983 EXPECT_TRUE(stored_event->HasNativeEvent());
984 EXPECT_TRUE(IsEqualXKeyEvent(*xevent, *(stored_event->native_event())));
985 EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
988 TEST_F(InputMethodChromeOSKeyEventTest, MultiKeyEventDelayResponseTest) {
989 // Preparation
990 input_type_ = TEXT_INPUT_TYPE_TEXT;
991 ime_->OnTextInputTypeChanged(this);
993 const int kFlags = ui::EF_SHIFT_DOWN;
994 ScopedXI2Event xevent;
995 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_B, kFlags);
996 const ui::KeyEvent event(xevent);
998 // Do key event.
999 ime_->DispatchKeyEvent(event);
1000 const ui::KeyEvent* key_event =
1001 mock_ime_engine_handler_->last_processed_key_event();
1002 EXPECT_EQ(ui::VKEY_B, key_event->key_code());
1003 EXPECT_EQ("KeyB", key_event->code());
1004 EXPECT_EQ(kFlags, key_event->flags());
1006 KeyEventCallback first_callback =
1007 mock_ime_engine_handler_->last_passed_callback();
1009 // Do key event again.
1010 ScopedXI2Event xevent2;
1011 xevent2.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_C, kFlags);
1012 const ui::KeyEvent event2(xevent2);
1014 ime_->DispatchKeyEvent(event2);
1015 const ui::KeyEvent* key_event2 =
1016 mock_ime_engine_handler_->last_processed_key_event();
1017 EXPECT_EQ(ui::VKEY_C, key_event2->key_code());
1018 EXPECT_EQ("KeyC", key_event2->code());
1019 EXPECT_EQ(kFlags, key_event2->flags());
1021 // Check before state.
1022 EXPECT_EQ(2,
1023 mock_ime_engine_handler_->process_key_event_call_count());
1024 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1026 // Do callback for first key event.
1027 first_callback.Run(true);
1029 // Check the results for first key event.
1030 EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
1031 const ui::KeyEvent* stored_event =
1032 ime_->process_key_event_post_ime_args().event;
1033 EXPECT_TRUE(stored_event->HasNativeEvent());
1034 EXPECT_TRUE(IsEqualXKeyEvent(*xevent, *(stored_event->native_event())));
1035 EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
1037 // Do callback for second key event.
1038 mock_ime_engine_handler_->last_passed_callback().Run(false);
1040 // Check the results for second key event.
1041 EXPECT_EQ(2, ime_->process_key_event_post_ime_call_count());
1042 stored_event = ime_->process_key_event_post_ime_args().event;
1043 EXPECT_TRUE(stored_event->HasNativeEvent());
1044 EXPECT_TRUE(IsEqualXKeyEvent(*xevent2, *(stored_event->native_event())));
1045 EXPECT_FALSE(ime_->process_key_event_post_ime_args().handled);
1048 TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseResetTest) {
1049 ScopedXI2Event xevent;
1050 xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_SHIFT_DOWN);
1051 const ui::KeyEvent event(xevent);
1053 // Do key event.
1054 input_type_ = TEXT_INPUT_TYPE_TEXT;
1055 ime_->OnTextInputTypeChanged(this);
1056 ime_->DispatchKeyEvent(event);
1058 // Check before state.
1059 EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count());
1060 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1062 ime_->ResetContext();
1064 // Do callback.
1065 mock_ime_engine_handler_->last_passed_callback().Run(true);
1067 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1069 // TODO(nona): Introduce ProcessKeyEventPostIME tests(crbug.com/156593).
1071 } // namespace ui