Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / base / ime / input_method_chromeos_unittest.cc
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.
4
5 #include "ui/base/ime/input_method_chromeos.h"
6
7 #include <X11/Xlib.h>
8 #undef Bool
9 #undef FocusIn
10 #undef FocusOut
11 #undef None
12
13 #include <cstring>
14
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"
28
29 using base::UTF8ToUTF16;
30 using base::UTF16ToUTF8;
31
32 namespace ui {
33 namespace {
34
35 const base::string16 kSampleText = base::UTF8ToUTF16(
36     "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
37
38 typedef chromeos::IMEEngineHandlerInterface::KeyEventDoneCallback
39     KeyEventCallback;
40
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();
48 }
49
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));
54   }
55   return false;
56 }
57
58 enum KeyEventHandlerBehavior {
59   KEYEVENT_CONSUME,
60   KEYEVENT_NOT_CONSUME,
61 };
62
63 }  // namespace
64
65
66 class TestableInputMethodChromeOS : public InputMethodChromeOS {
67  public:
68   explicit TestableInputMethodChromeOS(internal::InputMethodDelegate* delegate)
69       : InputMethodChromeOS(delegate),
70         process_key_event_post_ime_call_count_(0) {
71   }
72
73   struct ProcessKeyEventPostIMEArgs {
74     ProcessKeyEventPostIMEArgs() : event(NULL), handled(false) {}
75     const ui::KeyEvent* event;
76     bool handled;
77   };
78
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_;
85   }
86
87   void ResetCallCount() {
88     process_key_event_post_ime_call_count_ = 0;
89   }
90
91   const ProcessKeyEventPostIMEArgs& process_key_event_post_ime_args() const {
92     return process_key_event_post_ime_args_;
93   }
94
95   int process_key_event_post_ime_call_count() const {
96     return process_key_event_post_ime_call_count_;
97   }
98
99   // Change access rights for testing.
100   using InputMethodChromeOS::ExtractCompositionText;
101   using InputMethodChromeOS::ResetContext;
102
103  private:
104   ProcessKeyEventPostIMEArgs process_key_event_post_ime_args_;
105   int process_key_event_post_ime_call_count_;
106 };
107
108 class SynchronousKeyEventHandler {
109  public:
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) {}
118
119   virtual ~SynchronousKeyEventHandler() {}
120
121   void Run(uint32 keyval,
122            uint32 keycode,
123            uint32 state,
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);
129   }
130
131  private:
132   const uint32 expected_keyval_;
133   const uint32 expected_keycode_;
134   const uint32 expected_state_;
135   const KeyEventHandlerBehavior behavior_;
136
137   DISALLOW_COPY_AND_ASSIGN(SynchronousKeyEventHandler);
138 };
139
140 class AsynchronousKeyEventHandler {
141  public:
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) {}
148
149   virtual ~AsynchronousKeyEventHandler() {}
150
151   void Run(uint32 keyval,
152            uint32 keycode,
153            uint32 state,
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;
159   }
160
161   void RunCallback(KeyEventHandlerBehavior behavior) {
162     callback_.Run(behavior == KEYEVENT_CONSUME);
163   }
164
165  private:
166   const uint32 expected_keyval_;
167   const uint32 expected_keycode_;
168   const uint32 expected_state_;
169   KeyEventCallback callback_;
170
171   DISALLOW_COPY_AND_ASSIGN(AsynchronousKeyEventHandler);
172 };
173
174 class SetSurroundingTextVerifier {
175  public:
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) {}
182
183   void Verify(const std::string& text,
184               uint32 cursor_pos,
185               uint32 anchor_pos) {
186     EXPECT_EQ(expected_surrounding_text_, text);
187     EXPECT_EQ(expected_cursor_position_, cursor_pos);
188     EXPECT_EQ(expected_anchor_position_, anchor_pos);
189   }
190
191  private:
192   const std::string expected_surrounding_text_;
193   const uint32 expected_cursor_position_;
194   const uint32 expected_anchor_position_;
195
196   DISALLOW_COPY_AND_ASSIGN(SetSurroundingTextVerifier);
197 };
198
199 class InputMethodChromeOSTest : public internal::InputMethodDelegate,
200                                 public testing::Test,
201                                 public TextInputClient {
202  public:
203   InputMethodChromeOSTest()
204       : dispatched_key_event_(ui::ET_UNKNOWN, ui::VKEY_UNKNOWN, 0, false) {
205     ResetFlags();
206   }
207
208   virtual ~InputMethodChromeOSTest() {
209   }
210
211   virtual void SetUp() OVERRIDE {
212     chromeos::IMEBridge::Initialize();
213
214     mock_ime_engine_handler_.reset(
215         new chromeos::MockIMEEngineHandler());
216     chromeos::IMEBridge::Get()->SetCurrentEngineHandler(
217         mock_ime_engine_handler_.get());
218
219     mock_ime_candidate_window_handler_.reset(
220         new chromeos::MockIMECandidateWindowHandler());
221     chromeos::IMEBridge::Get()->SetCandidateWindowHandler(
222         mock_ime_candidate_window_handler_.get());
223
224     ime_.reset(new TestableInputMethodChromeOS(this));
225     ime_->SetFocusedTextInputClient(this);
226   }
227
228   virtual void TearDown() OVERRIDE {
229     if (ime_.get())
230       ime_->SetFocusedTextInputClient(NULL);
231     ime_.reset();
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();
237   }
238
239   // Overridden from ui::internal::InputMethodDelegate:
240   virtual bool DispatchKeyEventPostIME(const ui::KeyEvent& event) OVERRIDE {
241     dispatched_key_event_ = event;
242     return false;
243   }
244
245   // Overridden from ui::TextInputClient:
246   virtual void SetCompositionText(
247       const CompositionText& composition) OVERRIDE {
248     composition_text_ = composition;
249   }
250   virtual void ConfirmCompositionText() OVERRIDE {
251     confirmed_text_ = composition_text_;
252     composition_text_.Clear();
253   }
254   virtual void ClearCompositionText() OVERRIDE {
255     composition_text_.Clear();
256   }
257   virtual void InsertText(const base::string16& text) OVERRIDE {
258     inserted_text_ = text;
259   }
260   virtual void InsertChar(base::char16 ch, int flags) OVERRIDE {
261     inserted_char_ = ch;
262     inserted_char_flags_ = flags;
263   }
264   virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE {
265     return static_cast<gfx::NativeWindow>(NULL);
266   }
267   virtual TextInputType GetTextInputType() const OVERRIDE {
268     return input_type_;
269   }
270   virtual TextInputMode GetTextInputMode() const OVERRIDE {
271     return input_mode_;
272   }
273   virtual bool CanComposeInline() const OVERRIDE {
274     return can_compose_inline_;
275   }
276   virtual gfx::Rect GetCaretBounds() const OVERRIDE {
277     return caret_bounds_;
278   }
279   virtual bool GetCompositionCharacterBounds(uint32 index,
280                                              gfx::Rect* rect) const OVERRIDE {
281     return false;
282   }
283   virtual bool HasCompositionText() const OVERRIDE {
284     CompositionText empty;
285     return composition_text_ != empty;
286   }
287   virtual bool GetTextRange(gfx::Range* range) const OVERRIDE {
288     *range = text_range_;
289     return true;
290   }
291   virtual bool GetCompositionTextRange(gfx::Range* range) const OVERRIDE {
292     return false;
293   }
294   virtual bool GetSelectionRange(gfx::Range* range) const OVERRIDE {
295     *range = selection_range_;
296     return true;
297   }
298
299   virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE {
300     return false;
301   }
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());
306     return true;
307   }
308   virtual void OnInputMethodChanged() OVERRIDE {
309     ++on_input_method_changed_call_count_;
310   }
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 {}
319
320   bool HasNativeEvent() const {
321     return dispatched_key_event_.HasNativeEvent();
322   }
323
324   void ResetFlags() {
325     dispatched_key_event_ = ui::KeyEvent(ui::ET_UNKNOWN, ui::VKEY_UNKNOWN, 0,
326                                          false);
327
328     composition_text_.Clear();
329     confirmed_text_.Clear();
330     inserted_text_.clear();
331     inserted_char_ = 0;
332     inserted_char_flags_ = 0;
333     on_input_method_changed_call_count_ = 0;
334
335     input_type_ = TEXT_INPUT_TYPE_NONE;
336     input_mode_ = TEXT_INPUT_MODE_DEFAULT;
337     can_compose_inline_ = true;
338     caret_bounds_ = gfx::Rect();
339   }
340
341   scoped_ptr<TestableInputMethodChromeOS> ime_;
342
343   // Copy of the dispatched key event.
344   ui::KeyEvent dispatched_key_event_;
345
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_;
354
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_;
363
364   scoped_ptr<chromeos::MockIMEEngineHandler> mock_ime_engine_handler_;
365   scoped_ptr<chromeos::MockIMECandidateWindowHandler>
366       mock_ime_candidate_window_handler_;
367
368   DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSTest);
369 };
370
371 // Tests public APIs in ui::InputMethod first.
372
373 TEST_F(InputMethodChromeOSTest, GetInputLocale) {
374   // ui::InputMethodChromeOS does not support the API.
375   ime_->Init(true);
376   EXPECT_EQ("", ime_->GetInputLocale());
377 }
378
379 TEST_F(InputMethodChromeOSTest, IsActive) {
380   ime_->Init(true);
381   // ui::InputMethodChromeOS always returns true.
382   EXPECT_TRUE(ime_->IsActive());
383 }
384
385 TEST_F(InputMethodChromeOSTest, GetInputTextType) {
386   ime_->Init(true);
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());
394 }
395
396 TEST_F(InputMethodChromeOSTest, CanComposeInline) {
397   ime_->Init(true);
398   EXPECT_TRUE(ime_->CanComposeInline());
399   can_compose_inline_ = false;
400   ime_->OnTextInputTypeChanged(this);
401   EXPECT_FALSE(ime_->CanComposeInline());
402 }
403
404 TEST_F(InputMethodChromeOSTest, GetTextInputClient) {
405   ime_->Init(true);
406   EXPECT_EQ(this, ime_->GetTextInputClient());
407   ime_->SetFocusedTextInputClient(NULL);
408   EXPECT_EQ(NULL, ime_->GetTextInputClient());
409 }
410
411 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedClient) {
412   ime_->Init(true);
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());
420
421   ime_->SetFocusedTextInputClient(this);
422   ime_->OnTextInputTypeChanged(this);
423   EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
424 }
425
426 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow) {
427   ime_->Init(true);
428   EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
429   ime_->OnBlur();
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());
435
436   ime_->OnFocus();
437   ime_->OnTextInputTypeChanged(this);
438   EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
439 }
440
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());
447
448   ime_->OnFocus();
449   ime_->OnTextInputTypeChanged(this);
450   EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
451 }
452
453 // Confirm that IBusClient::FocusIn is called on "connected" if input_type_ is
454 // TEXT.
455 TEST_F(InputMethodChromeOSTest, FocusIn_Text) {
456   ime_->Init(true);
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());
464   EXPECT_EQ(
465       1,
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_);
471 }
472
473 // Confirm that InputMethodEngine::FocusIn is called on "connected" even if
474 // input_type_ is PASSWORD.
475 TEST_F(InputMethodChromeOSTest, FocusIn_Password) {
476   ime_->Init(true);
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_);
483 }
484
485 // Confirm that IBusClient::FocusOut is called as expected.
486 TEST_F(InputMethodChromeOSTest, FocusOut_None) {
487   input_type_ = TEXT_INPUT_TYPE_TEXT;
488   ime_->Init(true);
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());
495 }
496
497 // Confirm that IBusClient::FocusOut is called as expected.
498 TEST_F(InputMethodChromeOSTest, FocusOut_Password) {
499   input_type_ = TEXT_INPUT_TYPE_TEXT;
500   ime_->Init(true);
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());
507 }
508
509 // FocusIn/FocusOut scenario test
510 TEST_F(InputMethodChromeOSTest, Focus_Scenario) {
511   ime_->Init(true);
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);
519
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);
531
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);
541
542   input_type_ = TEXT_INPUT_TYPE_URL;
543   ime_->OnTextInputTypeChanged(this);
544   // Confirm that both FocusIn and FocusOut are called and the TextInputType is
545   // changed to URL.
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());
560 }
561
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;
565   ime_->Init(true);
566   EXPECT_EQ(
567       1,
568       mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
569   caret_bounds_ = gfx::Rect(1, 2, 3, 4);
570   ime_->OnCaretBoundsChanged(this);
571   EXPECT_EQ(
572       2,
573       mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
574   caret_bounds_ = gfx::Rect(0, 2, 3, 4);
575   ime_->OnCaretBoundsChanged(this);
576   EXPECT_EQ(
577       3,
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.
583   EXPECT_EQ(
584       4,
585       mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
586 }
587
588 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_NoAttribute) {
589   const base::string16 kSampleAsciiText = UTF8ToUTF16("Sample Text");
590   const uint32 kCursorPos = 2UL;
591
592   chromeos::CompositionText chromeos_composition_text;
593   chromeos_composition_text.set_text(kSampleAsciiText);
594
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);
608 }
609
610 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_SingleUnderline) {
611   const uint32 kCursorPos = 2UL;
612
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(
621       underline);
622
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);
638 }
639
640 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_DoubleUnderline) {
641   const uint32 kCursorPos = 2UL;
642
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(
651       underline);
652
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);
668 }
669
670 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_ErrorUnderline) {
671   const uint32 kCursorPos = 2UL;
672
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(
681       underline);
682
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);
697 }
698
699 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_Selection) {
700   const uint32 kCursorPos = 2UL;
701
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);
707
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);
723 }
724
725 TEST_F(InputMethodChromeOSTest,
726        ExtractCompositionTextTest_SelectionStartWithCursor) {
727   const uint32 kCursorPos = 1UL;
728
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);
734
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);
755 }
756
757 TEST_F(InputMethodChromeOSTest,
758        ExtractCompositionTextTest_SelectionEndWithCursor) {
759   const uint32 kCursorPos = 4UL;
760
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);
766
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);
787 }
788
789 TEST_F(InputMethodChromeOSTest, SurroundingText_NoSelectionTest) {
790   ime_->Init(true);
791   // Click a text input form.
792   input_type_ = TEXT_INPUT_TYPE_TEXT;
793   ime_->OnTextInputTypeChanged(this);
794
795   // Set the TextInputClient behaviors.
796   surrounding_text_ = UTF8ToUTF16("abcdef");
797   text_range_ = gfx::Range(0, 6);
798   selection_range_ = gfx::Range(3, 3);
799
800   // Set the verifier for SetSurroundingText mock call.
801   SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 3, 3);
802
803
804   ime_->OnCaretBoundsChanged(this);
805
806   // Check the call count.
807   EXPECT_EQ(1,
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());
811   EXPECT_EQ(3U,
812             mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
813   EXPECT_EQ(3U,
814             mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
815 }
816
817 TEST_F(InputMethodChromeOSTest, SurroundingText_SelectionTest) {
818   ime_->Init(true);
819   // Click a text input form.
820   input_type_ = TEXT_INPUT_TYPE_TEXT;
821   ime_->OnTextInputTypeChanged(this);
822
823   // Set the TextInputClient behaviors.
824   surrounding_text_ = UTF8ToUTF16("abcdef");
825   text_range_ = gfx::Range(0, 6);
826   selection_range_ = gfx::Range(2, 5);
827
828   // Set the verifier for SetSurroundingText mock call.
829   SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 2, 5);
830
831   ime_->OnCaretBoundsChanged(this);
832
833   // Check the call count.
834   EXPECT_EQ(1,
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());
838   EXPECT_EQ(2U,
839             mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
840   EXPECT_EQ(5U,
841             mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
842 }
843
844 TEST_F(InputMethodChromeOSTest, SurroundingText_PartialText) {
845   ime_->Init(true);
846   // Click a text input form.
847   input_type_ = TEXT_INPUT_TYPE_TEXT;
848   ime_->OnTextInputTypeChanged(this);
849
850   // Set the TextInputClient behaviors.
851   surrounding_text_ = UTF8ToUTF16("abcdefghij");
852   text_range_ = gfx::Range(5, 10);
853   selection_range_ = gfx::Range(7, 9);
854
855   ime_->OnCaretBoundsChanged(this);
856
857   // Check the call count.
858   EXPECT_EQ(1,
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.
862   EXPECT_EQ("fghij",
863             mock_ime_engine_handler_->last_set_surrounding_text());
864   EXPECT_EQ(2U,
865             mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
866   EXPECT_EQ(4U,
867             mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
868 }
869
870 TEST_F(InputMethodChromeOSTest, SurroundingText_BecomeEmptyText) {
871   ime_->Init(true);
872   // Click a text input form.
873   input_type_ = TEXT_INPUT_TYPE_TEXT;
874   ime_->OnTextInputTypeChanged(this);
875
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();
882
883   ime_->OnCaretBoundsChanged(this);
884
885   // Check the call count.
886   EXPECT_EQ(0,
887             mock_ime_engine_handler_->set_surrounding_text_call_count());
888
889   // Should not be called twice with same condition.
890   ime_->OnCaretBoundsChanged(this);
891   EXPECT_EQ(0,
892             mock_ime_engine_handler_->set_surrounding_text_call_count());
893 }
894
895 class InputMethodChromeOSKeyEventTest : public InputMethodChromeOSTest {
896  public:
897   InputMethodChromeOSKeyEventTest() {}
898   virtual ~InputMethodChromeOSKeyEventTest() {}
899
900   virtual void SetUp() OVERRIDE {
901     InputMethodChromeOSTest::SetUp();
902     ime_->Init(true);
903   }
904
905   DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSKeyEventTest);
906 };
907
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);
913
914   // Do key event.
915   input_type_ = TEXT_INPUT_TYPE_TEXT;
916   ime_->OnTextInputTypeChanged(this);
917   ime_->DispatchKeyEvent(event);
918
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());
927
928   // Do callback.
929   mock_ime_engine_handler_->last_passed_callback().Run(true);
930
931   // Check the results
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);
938 }
939
940 TEST_F(InputMethodChromeOSKeyEventTest, MultiKeyEventDelayResponseTest) {
941   // Preparation
942   input_type_ = TEXT_INPUT_TYPE_TEXT;
943   ime_->OnTextInputTypeChanged(this);
944
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);
949
950   // Do key event.
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());
957
958   KeyEventCallback first_callback =
959       mock_ime_engine_handler_->last_passed_callback();
960
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);
965
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());
972
973   // Check before state.
974   EXPECT_EQ(2,
975             mock_ime_engine_handler_->process_key_event_call_count());
976   EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
977
978   // Do callback for first key event.
979   first_callback.Run(true);
980
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);
988
989   // Do callback for second key event.
990   mock_ime_engine_handler_->last_passed_callback().Run(false);
991
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);
998 }
999
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);
1004
1005   // Do key event.
1006   input_type_ = TEXT_INPUT_TYPE_TEXT;
1007   ime_->OnTextInputTypeChanged(this);
1008   ime_->DispatchKeyEvent(event);
1009
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());
1013
1014   ime_->ResetContext();
1015
1016   // Do callback.
1017   mock_ime_engine_handler_->last_passed_callback().Run(true);
1018
1019   EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1020 }
1021 // TODO(nona): Introduce ProcessKeyEventPostIME tests(crbug.com/156593).
1022
1023 }  // namespace ui