Upstream version 5.34.104.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 "chromeos/ime/ibus_keymap.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "ui/base/ime/chromeos/ime_bridge.h"
22 #include "ui/base/ime/chromeos/mock_ime_candidate_window_handler.h"
23 #include "ui/base/ime/chromeos/mock_ime_engine_handler.h"
24 #include "ui/base/ime/input_method_delegate.h"
25 #include "ui/base/ime/text_input_client.h"
26 #include "ui/events/event.h"
27 #include "ui/events/test/events_test_utils_x11.h"
28 #include "ui/gfx/geometry/rect.h"
29
30 using base::UTF8ToUTF16;
31 using base::UTF16ToUTF8;
32
33 namespace ui {
34 namespace {
35 typedef chromeos::IMEEngineHandlerInterface::KeyEventDoneCallback
36     KeyEventCallback;
37
38 uint32 GetOffsetInUTF16(
39     const base::string16& utf16_string, uint32 utf8_offset) {
40   DCHECK_LT(utf8_offset, utf16_string.size());
41   base::i18n::UTF16CharIterator char_iterator(&utf16_string);
42   for (size_t i = 0; i < utf8_offset; ++i)
43     char_iterator.Advance();
44   return char_iterator.array_pos();
45 }
46
47 bool IsEqualXKeyEvent(const XEvent& e1, const XEvent& e2) {
48   if ((e1.type == KeyPress && e2.type == KeyPress) ||
49       (e1.type == KeyRelease && e2.type == KeyRelease)) {
50     return !std::memcmp(&e1.xkey, &e2.xkey, sizeof(XKeyEvent));
51   }
52   return false;
53 }
54
55 enum KeyEventHandlerBehavior {
56   KEYEVENT_CONSUME,
57   KEYEVENT_NOT_CONSUME,
58 };
59
60 }  // namespace
61
62
63 class TestableInputMethodChromeOS : public InputMethodChromeOS {
64  public:
65   explicit TestableInputMethodChromeOS(internal::InputMethodDelegate* delegate)
66       : InputMethodChromeOS(delegate),
67         process_key_event_post_ime_call_count_(0) {
68   }
69
70   struct ProcessKeyEventPostIMEArgs {
71     ProcessKeyEventPostIMEArgs() : event(NULL), handled(false) {}
72     const ui::KeyEvent* event;
73     bool handled;
74   };
75
76   // Overridden from InputMethodChromeOS:
77   virtual void ProcessKeyEventPostIME(const ui::KeyEvent& key_event,
78                                       bool handled) OVERRIDE {
79     process_key_event_post_ime_args_.event = &key_event;
80     process_key_event_post_ime_args_.handled = handled;
81     ++process_key_event_post_ime_call_count_;
82   }
83
84   void ResetCallCount() {
85     process_key_event_post_ime_call_count_ = 0;
86   }
87
88   const ProcessKeyEventPostIMEArgs& process_key_event_post_ime_args() const {
89     return process_key_event_post_ime_args_;
90   }
91
92   int process_key_event_post_ime_call_count() const {
93     return process_key_event_post_ime_call_count_;
94   }
95
96   // Change access rights for testing.
97   using InputMethodChromeOS::ExtractCompositionText;
98   using InputMethodChromeOS::ResetContext;
99
100  private:
101   ProcessKeyEventPostIMEArgs process_key_event_post_ime_args_;
102   int process_key_event_post_ime_call_count_;
103 };
104
105 class SynchronousKeyEventHandler {
106  public:
107   SynchronousKeyEventHandler(uint32 expected_keyval,
108                              uint32 expected_keycode,
109                              uint32 expected_state,
110                              KeyEventHandlerBehavior behavior)
111       : expected_keyval_(expected_keyval),
112         expected_keycode_(expected_keycode),
113         expected_state_(expected_state),
114         behavior_(behavior) {}
115
116   virtual ~SynchronousKeyEventHandler() {}
117
118   void Run(uint32 keyval,
119            uint32 keycode,
120            uint32 state,
121            const KeyEventCallback& callback) {
122     EXPECT_EQ(expected_keyval_, keyval);
123     EXPECT_EQ(expected_keycode_, keycode);
124     EXPECT_EQ(expected_state_, state);
125     callback.Run(behavior_ == KEYEVENT_CONSUME);
126   }
127
128  private:
129   const uint32 expected_keyval_;
130   const uint32 expected_keycode_;
131   const uint32 expected_state_;
132   const KeyEventHandlerBehavior behavior_;
133
134   DISALLOW_COPY_AND_ASSIGN(SynchronousKeyEventHandler);
135 };
136
137 class AsynchronousKeyEventHandler {
138  public:
139   AsynchronousKeyEventHandler(uint32 expected_keyval,
140                               uint32 expected_keycode,
141                               uint32 expected_state)
142       : expected_keyval_(expected_keyval),
143         expected_keycode_(expected_keycode),
144         expected_state_(expected_state) {}
145
146   virtual ~AsynchronousKeyEventHandler() {}
147
148   void Run(uint32 keyval,
149            uint32 keycode,
150            uint32 state,
151            const KeyEventCallback& callback) {
152     EXPECT_EQ(expected_keyval_, keyval);
153     EXPECT_EQ(expected_keycode_, keycode);
154     EXPECT_EQ(expected_state_, state);
155     callback_ = callback;
156   }
157
158   void RunCallback(KeyEventHandlerBehavior behavior) {
159     callback_.Run(behavior == KEYEVENT_CONSUME);
160   }
161
162  private:
163   const uint32 expected_keyval_;
164   const uint32 expected_keycode_;
165   const uint32 expected_state_;
166   KeyEventCallback callback_;
167
168   DISALLOW_COPY_AND_ASSIGN(AsynchronousKeyEventHandler);
169 };
170
171 class SetSurroundingTextVerifier {
172  public:
173   SetSurroundingTextVerifier(const std::string& expected_surrounding_text,
174                              uint32 expected_cursor_position,
175                              uint32 expected_anchor_position)
176       : expected_surrounding_text_(expected_surrounding_text),
177         expected_cursor_position_(expected_cursor_position),
178         expected_anchor_position_(expected_anchor_position) {}
179
180   void Verify(const std::string& text,
181               uint32 cursor_pos,
182               uint32 anchor_pos) {
183     EXPECT_EQ(expected_surrounding_text_, text);
184     EXPECT_EQ(expected_cursor_position_, cursor_pos);
185     EXPECT_EQ(expected_anchor_position_, anchor_pos);
186   }
187
188  private:
189   const std::string expected_surrounding_text_;
190   const uint32 expected_cursor_position_;
191   const uint32 expected_anchor_position_;
192
193   DISALLOW_COPY_AND_ASSIGN(SetSurroundingTextVerifier);
194 };
195
196 class InputMethodChromeOSTest : public internal::InputMethodDelegate,
197                                 public testing::Test,
198                                 public TextInputClient {
199  public:
200   InputMethodChromeOSTest()
201       : dispatched_key_event_(ui::ET_UNKNOWN, ui::VKEY_UNKNOWN, 0, false) {
202     ResetFlags();
203   }
204
205   virtual ~InputMethodChromeOSTest() {
206   }
207
208   virtual void SetUp() OVERRIDE {
209     chromeos::IMEBridge::Initialize();
210
211     mock_ime_engine_handler_.reset(
212         new chromeos::MockIMEEngineHandler());
213     chromeos::IMEBridge::Get()->SetCurrentEngineHandler(
214         mock_ime_engine_handler_.get());
215
216     mock_ime_candidate_window_handler_.reset(
217         new chromeos::MockIMECandidateWindowHandler());
218     chromeos::IMEBridge::Get()->SetCandidateWindowHandler(
219         mock_ime_candidate_window_handler_.get());
220
221     ime_.reset(new TestableInputMethodChromeOS(this));
222     ime_->SetFocusedTextInputClient(this);
223   }
224
225   virtual void TearDown() OVERRIDE {
226     if (ime_.get())
227       ime_->SetFocusedTextInputClient(NULL);
228     ime_.reset();
229     chromeos::IMEBridge::Get()->SetCurrentEngineHandler(NULL);
230     chromeos::IMEBridge::Get()->SetCandidateWindowHandler(NULL);
231     mock_ime_engine_handler_.reset();
232     mock_ime_candidate_window_handler_.reset();
233     chromeos::IMEBridge::Shutdown();
234   }
235
236   // Overridden from ui::internal::InputMethodDelegate:
237   virtual bool DispatchKeyEventPostIME(const ui::KeyEvent& event) OVERRIDE {
238     dispatched_key_event_ = event;
239     return false;
240   }
241
242   // Overridden from ui::TextInputClient:
243   virtual void SetCompositionText(
244       const CompositionText& composition) OVERRIDE {
245     composition_text_ = composition;
246   }
247   virtual void ConfirmCompositionText() OVERRIDE {
248     confirmed_text_ = composition_text_;
249     composition_text_.Clear();
250   }
251   virtual void ClearCompositionText() OVERRIDE {
252     composition_text_.Clear();
253   }
254   virtual void InsertText(const base::string16& text) OVERRIDE {
255     inserted_text_ = text;
256   }
257   virtual void InsertChar(base::char16 ch, int flags) OVERRIDE {
258     inserted_char_ = ch;
259     inserted_char_flags_ = flags;
260   }
261   virtual gfx::NativeWindow GetAttachedWindow() const OVERRIDE {
262     return static_cast<gfx::NativeWindow>(NULL);
263   }
264   virtual TextInputType GetTextInputType() const OVERRIDE {
265     return input_type_;
266   }
267   virtual TextInputMode GetTextInputMode() const OVERRIDE {
268     return input_mode_;
269   }
270   virtual bool CanComposeInline() const OVERRIDE {
271     return can_compose_inline_;
272   }
273   virtual gfx::Rect GetCaretBounds() const OVERRIDE {
274     return caret_bounds_;
275   }
276   virtual bool GetCompositionCharacterBounds(uint32 index,
277                                              gfx::Rect* rect) const OVERRIDE {
278     return false;
279   }
280   virtual bool HasCompositionText() const OVERRIDE {
281     CompositionText empty;
282     return composition_text_ != empty;
283   }
284   virtual bool GetTextRange(gfx::Range* range) const OVERRIDE {
285     *range = text_range_;
286     return true;
287   }
288   virtual bool GetCompositionTextRange(gfx::Range* range) const OVERRIDE {
289     return false;
290   }
291   virtual bool GetSelectionRange(gfx::Range* range) const OVERRIDE {
292     *range = selection_range_;
293     return true;
294   }
295
296   virtual bool SetSelectionRange(const gfx::Range& range) OVERRIDE {
297     return false;
298   }
299   virtual bool DeleteRange(const gfx::Range& range) OVERRIDE { return false; }
300   virtual bool GetTextFromRange(const gfx::Range& range,
301                                 base::string16* text) const OVERRIDE {
302     *text = surrounding_text_.substr(range.GetMin(), range.length());
303     return true;
304   }
305   virtual void OnInputMethodChanged() OVERRIDE {
306     ++on_input_method_changed_call_count_;
307   }
308   virtual bool ChangeTextDirectionAndLayoutAlignment(
309       base::i18n::TextDirection direction) OVERRIDE { return false; }
310   virtual void ExtendSelectionAndDelete(size_t before,
311                                         size_t after) OVERRIDE {}
312   virtual void EnsureCaretInRect(const gfx::Rect& rect) OVERRIDE {}
313   virtual void OnCandidateWindowShown() OVERRIDE {}
314   virtual void OnCandidateWindowUpdated() OVERRIDE {}
315   virtual void OnCandidateWindowHidden() OVERRIDE {}
316
317   bool HasNativeEvent() const {
318     return dispatched_key_event_.HasNativeEvent();
319   }
320
321   void ResetFlags() {
322     dispatched_key_event_ = ui::KeyEvent(ui::ET_UNKNOWN, ui::VKEY_UNKNOWN, 0,
323                                          false);
324
325     composition_text_.Clear();
326     confirmed_text_.Clear();
327     inserted_text_.clear();
328     inserted_char_ = 0;
329     inserted_char_flags_ = 0;
330     on_input_method_changed_call_count_ = 0;
331
332     input_type_ = TEXT_INPUT_TYPE_NONE;
333     input_mode_ = TEXT_INPUT_MODE_DEFAULT;
334     can_compose_inline_ = true;
335     caret_bounds_ = gfx::Rect();
336   }
337
338   scoped_ptr<TestableInputMethodChromeOS> ime_;
339
340   // Copy of the dispatched key event.
341   ui::KeyEvent dispatched_key_event_;
342
343   // Variables for remembering the parameters that are passed to
344   // ui::TextInputClient functions.
345   CompositionText composition_text_;
346   CompositionText confirmed_text_;
347   base::string16 inserted_text_;
348   base::char16 inserted_char_;
349   unsigned int on_input_method_changed_call_count_;
350   int inserted_char_flags_;
351
352   // Variables that will be returned from the ui::TextInputClient functions.
353   TextInputType input_type_;
354   TextInputMode input_mode_;
355   bool can_compose_inline_;
356   gfx::Rect caret_bounds_;
357   gfx::Range text_range_;
358   gfx::Range selection_range_;
359   base::string16 surrounding_text_;
360
361   scoped_ptr<chromeos::MockIMEEngineHandler> mock_ime_engine_handler_;
362   scoped_ptr<chromeos::MockIMECandidateWindowHandler>
363       mock_ime_candidate_window_handler_;
364
365   DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSTest);
366 };
367
368 // Tests public APIs in ui::InputMethod first.
369
370 TEST_F(InputMethodChromeOSTest, GetInputLocale) {
371   // ui::InputMethodChromeOS does not support the API.
372   ime_->Init(true);
373   EXPECT_EQ("", ime_->GetInputLocale());
374 }
375
376 TEST_F(InputMethodChromeOSTest, IsActive) {
377   ime_->Init(true);
378   // ui::InputMethodChromeOS always returns true.
379   EXPECT_TRUE(ime_->IsActive());
380 }
381
382 TEST_F(InputMethodChromeOSTest, GetInputTextType) {
383   ime_->Init(true);
384   EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
385   input_type_ = TEXT_INPUT_TYPE_PASSWORD;
386   ime_->OnTextInputTypeChanged(this);
387   EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
388   input_type_ = TEXT_INPUT_TYPE_TEXT;
389   ime_->OnTextInputTypeChanged(this);
390   EXPECT_EQ(TEXT_INPUT_TYPE_TEXT, ime_->GetTextInputType());
391 }
392
393 TEST_F(InputMethodChromeOSTest, CanComposeInline) {
394   ime_->Init(true);
395   EXPECT_TRUE(ime_->CanComposeInline());
396   can_compose_inline_ = false;
397   ime_->OnTextInputTypeChanged(this);
398   EXPECT_FALSE(ime_->CanComposeInline());
399 }
400
401 TEST_F(InputMethodChromeOSTest, GetTextInputClient) {
402   ime_->Init(true);
403   EXPECT_EQ(this, ime_->GetTextInputClient());
404   ime_->SetFocusedTextInputClient(NULL);
405   EXPECT_EQ(NULL, ime_->GetTextInputClient());
406 }
407
408 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedClient) {
409   ime_->Init(true);
410   EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
411   ime_->SetFocusedTextInputClient(NULL);
412   input_type_ = TEXT_INPUT_TYPE_PASSWORD;
413   ime_->OnTextInputTypeChanged(this);
414   // The OnTextInputTypeChanged() call above should be ignored since |this| is
415   // not the current focused client.
416   EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
417
418   ime_->SetFocusedTextInputClient(this);
419   ime_->OnTextInputTypeChanged(this);
420   EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
421 }
422
423 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow) {
424   ime_->Init(true);
425   EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
426   ime_->OnBlur();
427   input_type_ = TEXT_INPUT_TYPE_PASSWORD;
428   ime_->OnTextInputTypeChanged(this);
429   // The OnTextInputTypeChanged() call above should be ignored since the top-
430   // level window which the ime_ is attached to is not currently focused.
431   EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
432
433   ime_->OnFocus();
434   ime_->OnTextInputTypeChanged(this);
435   EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
436 }
437
438 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow2) {
439   ime_->Init(false);  // the top-level is initially unfocused.
440   EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
441   input_type_ = TEXT_INPUT_TYPE_PASSWORD;
442   ime_->OnTextInputTypeChanged(this);
443   EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
444
445   ime_->OnFocus();
446   ime_->OnTextInputTypeChanged(this);
447   EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
448 }
449
450 // Confirm that IBusClient::FocusIn is called on "connected" if input_type_ is
451 // TEXT.
452 TEST_F(InputMethodChromeOSTest, FocusIn_Text) {
453   ime_->Init(true);
454   // A context shouldn't be created since the daemon is not running.
455   EXPECT_EQ(0U, on_input_method_changed_call_count_);
456   // Click a text input form.
457   input_type_ = TEXT_INPUT_TYPE_TEXT;
458   ime_->OnTextInputTypeChanged(this);
459   // Since a form has focus, IBusClient::FocusIn() should be called.
460   EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
461   EXPECT_EQ(
462       1,
463       mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
464   // ui::TextInputClient::OnInputMethodChanged() should be called when
465   // ui::InputMethodChromeOS connects/disconnects to/from ibus-daemon and the
466   // current text input type is not NONE.
467   EXPECT_EQ(1U, on_input_method_changed_call_count_);
468 }
469
470 // Confirm that IBusClient::FocusIn is NOT called on "connected" if input_type_
471 // is PASSWORD.
472 TEST_F(InputMethodChromeOSTest, FocusIn_Password) {
473   ime_->Init(true);
474   EXPECT_EQ(0U, on_input_method_changed_call_count_);
475   input_type_ = TEXT_INPUT_TYPE_PASSWORD;
476   ime_->OnTextInputTypeChanged(this);
477   // Since a form has focus, IBusClient::FocusIn() should NOT be called.
478   EXPECT_EQ(0, mock_ime_engine_handler_->focus_in_call_count());
479   EXPECT_EQ(1U, on_input_method_changed_call_count_);
480 }
481
482 // Confirm that IBusClient::FocusOut is called as expected.
483 TEST_F(InputMethodChromeOSTest, FocusOut_None) {
484   input_type_ = TEXT_INPUT_TYPE_TEXT;
485   ime_->Init(true);
486   EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
487   EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
488   input_type_ = TEXT_INPUT_TYPE_NONE;
489   ime_->OnTextInputTypeChanged(this);
490   EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
491   EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
492 }
493
494 // Confirm that IBusClient::FocusOut is called as expected.
495 TEST_F(InputMethodChromeOSTest, FocusOut_Password) {
496   input_type_ = TEXT_INPUT_TYPE_TEXT;
497   ime_->Init(true);
498   EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
499   EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
500   input_type_ = TEXT_INPUT_TYPE_PASSWORD;
501   ime_->OnTextInputTypeChanged(this);
502   EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
503   EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
504 }
505
506 // FocusIn/FocusOut scenario test
507 TEST_F(InputMethodChromeOSTest, Focus_Scenario) {
508   ime_->Init(true);
509   // Confirm that both FocusIn and FocusOut are NOT called.
510   EXPECT_EQ(0, mock_ime_engine_handler_->focus_in_call_count());
511   EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
512   EXPECT_EQ(TEXT_INPUT_TYPE_NONE,
513             mock_ime_engine_handler_->last_text_input_context().type);
514   EXPECT_EQ(TEXT_INPUT_MODE_DEFAULT,
515             mock_ime_engine_handler_->last_text_input_context().mode);
516
517   input_type_ = TEXT_INPUT_TYPE_TEXT;
518   input_mode_ = TEXT_INPUT_MODE_LATIN;
519   ime_->OnTextInputTypeChanged(this);
520   // Confirm that only FocusIn is called, the TextInputType is TEXT and the
521   // TextInputMode is LATIN..
522   EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
523   EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
524   EXPECT_EQ(TEXT_INPUT_TYPE_TEXT,
525             mock_ime_engine_handler_->last_text_input_context().type);
526   EXPECT_EQ(TEXT_INPUT_MODE_LATIN,
527             mock_ime_engine_handler_->last_text_input_context().mode);
528
529   input_mode_ = TEXT_INPUT_MODE_KANA;
530   ime_->OnTextInputTypeChanged(this);
531   // Confirm that both FocusIn and FocusOut are NOT called.
532   EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
533   EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
534   EXPECT_EQ(TEXT_INPUT_TYPE_TEXT,
535             mock_ime_engine_handler_->last_text_input_context().type);
536   EXPECT_EQ(TEXT_INPUT_MODE_LATIN,
537             mock_ime_engine_handler_->last_text_input_context().mode);
538
539   input_type_ = TEXT_INPUT_TYPE_URL;
540   ime_->OnTextInputTypeChanged(this);
541   // Confirm that both FocusIn and FocusOut are called and the TextInputType is
542   // URL.
543   EXPECT_EQ(2, mock_ime_engine_handler_->focus_in_call_count());
544   EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
545   EXPECT_EQ(TEXT_INPUT_TYPE_URL,
546             mock_ime_engine_handler_->last_text_input_context().type);
547   EXPECT_EQ(TEXT_INPUT_MODE_KANA,
548             mock_ime_engine_handler_->last_text_input_context().mode);
549 }
550
551 // Test if the new |caret_bounds_| is correctly sent to ibus-daemon.
552 TEST_F(InputMethodChromeOSTest, OnCaretBoundsChanged) {
553   input_type_ = TEXT_INPUT_TYPE_TEXT;
554   ime_->Init(true);
555   EXPECT_EQ(
556       1,
557       mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
558   caret_bounds_ = gfx::Rect(1, 2, 3, 4);
559   ime_->OnCaretBoundsChanged(this);
560   EXPECT_EQ(
561       2,
562       mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
563   caret_bounds_ = gfx::Rect(0, 2, 3, 4);
564   ime_->OnCaretBoundsChanged(this);
565   EXPECT_EQ(
566       3,
567       mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
568   caret_bounds_ = gfx::Rect(0, 2, 3, 4);  // unchanged
569   ime_->OnCaretBoundsChanged(this);
570   // Current InputMethodChromeOS implementation performs the IPC
571   // regardless of the bounds are changed or not.
572   EXPECT_EQ(
573       4,
574       mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
575 }
576
577 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_NoAttribute) {
578   const base::string16 kSampleText = base::UTF8ToUTF16("Sample Text");
579   const uint32 kCursorPos = 2UL;
580
581   chromeos::CompositionText chromeos_composition_text;
582   chromeos_composition_text.set_text(kSampleText);
583
584   CompositionText composition_text;
585   ime_->ExtractCompositionText(
586       chromeos_composition_text, kCursorPos, &composition_text);
587   EXPECT_EQ(kSampleText, composition_text.text);
588   // If there is no selection, |selection| represents cursor position.
589   EXPECT_EQ(kCursorPos, composition_text.selection.start());
590   EXPECT_EQ(kCursorPos, composition_text.selection.end());
591   // If there is no underline, |underlines| contains one underline and it is
592   // whole text underline.
593   ASSERT_EQ(1UL, composition_text.underlines.size());
594   EXPECT_EQ(0UL, composition_text.underlines[0].start_offset);
595   EXPECT_EQ(kSampleText.size(), composition_text.underlines[0].end_offset);
596   EXPECT_FALSE(composition_text.underlines[0].thick);
597 }
598
599 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_SingleUnderline) {
600   const base::string16 kSampleText = base::UTF8ToUTF16(
601       "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
602   const uint32 kCursorPos = 2UL;
603
604   // Set up chromeos composition text with one underline attribute.
605   chromeos::CompositionText chromeos_composition_text;
606   chromeos_composition_text.set_text(kSampleText);
607   chromeos::CompositionText::UnderlineAttribute underline;
608   underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_SINGLE;
609   underline.start_index = 1UL;
610   underline.end_index = 4UL;
611   chromeos_composition_text.mutable_underline_attributes()->push_back(
612       underline);
613
614   CompositionText composition_text;
615   ime_->ExtractCompositionText(
616       chromeos_composition_text, kCursorPos, &composition_text);
617   EXPECT_EQ(kSampleText, composition_text.text);
618   // If there is no selection, |selection| represents cursor position.
619   EXPECT_EQ(kCursorPos, composition_text.selection.start());
620   EXPECT_EQ(kCursorPos, composition_text.selection.end());
621   ASSERT_EQ(1UL, composition_text.underlines.size());
622   EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
623             composition_text.underlines[0].start_offset);
624   EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
625             composition_text.underlines[0].end_offset);
626   // Single underline represents as black thin line.
627   EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
628   EXPECT_FALSE(composition_text.underlines[0].thick);
629 }
630
631 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_DoubleUnderline) {
632   const base::string16 kSampleText = base::UTF8ToUTF16(
633       "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
634   const uint32 kCursorPos = 2UL;
635
636   // Set up chromeos composition text with one underline attribute.
637   chromeos::CompositionText chromeos_composition_text;
638   chromeos_composition_text.set_text(kSampleText);
639   chromeos::CompositionText::UnderlineAttribute underline;
640   underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_DOUBLE;
641   underline.start_index = 1UL;
642   underline.end_index = 4UL;
643   chromeos_composition_text.mutable_underline_attributes()->push_back(
644       underline);
645
646   CompositionText composition_text;
647   ime_->ExtractCompositionText(
648       chromeos_composition_text, kCursorPos, &composition_text);
649   EXPECT_EQ(kSampleText, composition_text.text);
650   // If there is no selection, |selection| represents cursor position.
651   EXPECT_EQ(kCursorPos, composition_text.selection.start());
652   EXPECT_EQ(kCursorPos, composition_text.selection.end());
653   ASSERT_EQ(1UL, composition_text.underlines.size());
654   EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
655             composition_text.underlines[0].start_offset);
656   EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
657             composition_text.underlines[0].end_offset);
658   // Double underline represents as black thick line.
659   EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
660   EXPECT_TRUE(composition_text.underlines[0].thick);
661 }
662
663 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_ErrorUnderline) {
664   const base::string16 kSampleText = base::UTF8ToUTF16(
665       "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
666   const uint32 kCursorPos = 2UL;
667
668   // Set up chromeos composition text with one underline attribute.
669   chromeos::CompositionText chromeos_composition_text;
670   chromeos_composition_text.set_text(kSampleText);
671   chromeos::CompositionText::UnderlineAttribute underline;
672   underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_ERROR;
673   underline.start_index = 1UL;
674   underline.end_index = 4UL;
675   chromeos_composition_text.mutable_underline_attributes()->push_back(
676       underline);
677
678   CompositionText composition_text;
679   ime_->ExtractCompositionText(
680       chromeos_composition_text, kCursorPos, &composition_text);
681   EXPECT_EQ(kSampleText, composition_text.text);
682   EXPECT_EQ(kCursorPos, composition_text.selection.start());
683   EXPECT_EQ(kCursorPos, composition_text.selection.end());
684   ASSERT_EQ(1UL, composition_text.underlines.size());
685   EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
686             composition_text.underlines[0].start_offset);
687   EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
688             composition_text.underlines[0].end_offset);
689   // Error underline represents as red thin line.
690   EXPECT_EQ(SK_ColorRED, composition_text.underlines[0].color);
691   EXPECT_FALSE(composition_text.underlines[0].thick);
692 }
693
694 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_Selection) {
695   const base::string16 kSampleText = base::UTF8ToUTF16(
696       "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
697   const uint32 kCursorPos = 2UL;
698
699   // Set up chromeos composition text with one underline attribute.
700   chromeos::CompositionText chromeos_composition_text;
701   chromeos_composition_text.set_text(kSampleText);
702   chromeos_composition_text.set_selection_start(1UL);
703   chromeos_composition_text.set_selection_end(4UL);
704
705   CompositionText composition_text;
706   ime_->ExtractCompositionText(
707       chromeos_composition_text, kCursorPos, &composition_text);
708   EXPECT_EQ(kSampleText, composition_text.text);
709   EXPECT_EQ(kCursorPos, composition_text.selection.start());
710   EXPECT_EQ(kCursorPos, composition_text.selection.end());
711   ASSERT_EQ(1UL, composition_text.underlines.size());
712   EXPECT_EQ(GetOffsetInUTF16(kSampleText,
713                              chromeos_composition_text.selection_start()),
714             composition_text.underlines[0].start_offset);
715   EXPECT_EQ(GetOffsetInUTF16(kSampleText,
716                              chromeos_composition_text.selection_end()),
717             composition_text.underlines[0].end_offset);
718   EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
719   EXPECT_TRUE(composition_text.underlines[0].thick);
720 }
721
722 TEST_F(InputMethodChromeOSTest,
723        ExtractCompositionTextTest_SelectionStartWithCursor) {
724   const base::string16 kSampleText = base::UTF8ToUTF16(
725       "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
726   const uint32 kCursorPos = 1UL;
727
728   // Set up chromeos composition text with one underline attribute.
729   chromeos::CompositionText chromeos_composition_text;
730   chromeos_composition_text.set_text(kSampleText);
731   chromeos_composition_text.set_selection_start(kCursorPos);
732   chromeos_composition_text.set_selection_end(4UL);
733
734   CompositionText composition_text;
735   ime_->ExtractCompositionText(
736       chromeos_composition_text, kCursorPos, &composition_text);
737   EXPECT_EQ(kSampleText, composition_text.text);
738   // If the cursor position is same as selection bounds, selection start
739   // position become opposit side of selection from cursor.
740   EXPECT_EQ(GetOffsetInUTF16(kSampleText,
741                              chromeos_composition_text.selection_end()),
742             composition_text.selection.start());
743   EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
744             composition_text.selection.end());
745   ASSERT_EQ(1UL, composition_text.underlines.size());
746   EXPECT_EQ(GetOffsetInUTF16(kSampleText,
747                              chromeos_composition_text.selection_start()),
748             composition_text.underlines[0].start_offset);
749   EXPECT_EQ(GetOffsetInUTF16(kSampleText,
750                              chromeos_composition_text.selection_end()),
751             composition_text.underlines[0].end_offset);
752   EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
753   EXPECT_TRUE(composition_text.underlines[0].thick);
754 }
755
756 TEST_F(InputMethodChromeOSTest,
757        ExtractCompositionTextTest_SelectionEndWithCursor) {
758   const base::string16 kSampleText = base::UTF8ToUTF16(
759       "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
760   const uint32 kCursorPos = 4UL;
761
762   // Set up chromeos composition text with one underline attribute.
763   chromeos::CompositionText chromeos_composition_text;
764   chromeos_composition_text.set_text(kSampleText);
765   chromeos_composition_text.set_selection_start(1UL);
766   chromeos_composition_text.set_selection_end(kCursorPos);
767
768   CompositionText composition_text;
769   ime_->ExtractCompositionText(
770       chromeos_composition_text, kCursorPos, &composition_text);
771   EXPECT_EQ(kSampleText, composition_text.text);
772   // If the cursor position is same as selection bounds, selection start
773   // position become opposit side of selection from cursor.
774   EXPECT_EQ(GetOffsetInUTF16(kSampleText,
775                              chromeos_composition_text.selection_start()),
776             composition_text.selection.start());
777   EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
778             composition_text.selection.end());
779   ASSERT_EQ(1UL, composition_text.underlines.size());
780   EXPECT_EQ(GetOffsetInUTF16(kSampleText,
781                              chromeos_composition_text.selection_start()),
782             composition_text.underlines[0].start_offset);
783   EXPECT_EQ(GetOffsetInUTF16(kSampleText,
784                              chromeos_composition_text.selection_end()),
785             composition_text.underlines[0].end_offset);
786   EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
787   EXPECT_TRUE(composition_text.underlines[0].thick);
788 }
789
790 TEST_F(InputMethodChromeOSTest, SurroundingText_NoSelectionTest) {
791   ime_->Init(true);
792   // Click a text input form.
793   input_type_ = TEXT_INPUT_TYPE_TEXT;
794   ime_->OnTextInputTypeChanged(this);
795
796   // Set the TextInputClient behaviors.
797   surrounding_text_ = UTF8ToUTF16("abcdef");
798   text_range_ = gfx::Range(0, 6);
799   selection_range_ = gfx::Range(3, 3);
800
801   // Set the verifier for SetSurroundingText mock call.
802   SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 3, 3);
803
804
805   ime_->OnCaretBoundsChanged(this);
806
807   // Check the call count.
808   EXPECT_EQ(1,
809             mock_ime_engine_handler_->set_surrounding_text_call_count());
810   EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
811             mock_ime_engine_handler_->last_set_surrounding_text());
812   EXPECT_EQ(3U,
813             mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
814   EXPECT_EQ(3U,
815             mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
816 }
817
818 TEST_F(InputMethodChromeOSTest, SurroundingText_SelectionTest) {
819   ime_->Init(true);
820   // Click a text input form.
821   input_type_ = TEXT_INPUT_TYPE_TEXT;
822   ime_->OnTextInputTypeChanged(this);
823
824   // Set the TextInputClient behaviors.
825   surrounding_text_ = UTF8ToUTF16("abcdef");
826   text_range_ = gfx::Range(0, 6);
827   selection_range_ = gfx::Range(2, 5);
828
829   // Set the verifier for SetSurroundingText mock call.
830   SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 2, 5);
831
832   ime_->OnCaretBoundsChanged(this);
833
834   // Check the call count.
835   EXPECT_EQ(1,
836             mock_ime_engine_handler_->set_surrounding_text_call_count());
837   EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
838             mock_ime_engine_handler_->last_set_surrounding_text());
839   EXPECT_EQ(2U,
840             mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
841   EXPECT_EQ(5U,
842             mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
843 }
844
845 TEST_F(InputMethodChromeOSTest, SurroundingText_PartialText) {
846   ime_->Init(true);
847   // Click a text input form.
848   input_type_ = TEXT_INPUT_TYPE_TEXT;
849   ime_->OnTextInputTypeChanged(this);
850
851   // Set the TextInputClient behaviors.
852   surrounding_text_ = base::UTF8ToUTF16("abcdefghij");
853   text_range_ = gfx::Range(5, 10);
854   selection_range_ = gfx::Range(7, 9);
855
856   ime_->OnCaretBoundsChanged(this);
857
858   // Check the call count.
859   EXPECT_EQ(1,
860             mock_ime_engine_handler_->set_surrounding_text_call_count());
861   // Set the verifier for SetSurroundingText mock call.
862   // Here (2, 4) is selection range in expected surrounding text coordinates.
863   EXPECT_EQ("fghij",
864             mock_ime_engine_handler_->last_set_surrounding_text());
865   EXPECT_EQ(2U,
866             mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
867   EXPECT_EQ(4U,
868             mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
869 }
870
871 TEST_F(InputMethodChromeOSTest, SurroundingText_BecomeEmptyText) {
872   ime_->Init(true);
873   // Click a text input form.
874   input_type_ = TEXT_INPUT_TYPE_TEXT;
875   ime_->OnTextInputTypeChanged(this);
876
877   // Set the TextInputClient behaviors.
878   // If the surrounding text becomes empty, text_range become (0, 0) and
879   // selection range become invalid.
880   surrounding_text_ = base::UTF8ToUTF16("");
881   text_range_ = gfx::Range(0, 0);
882   selection_range_ = gfx::Range::InvalidRange();
883
884   ime_->OnCaretBoundsChanged(this);
885
886   // Check the call count.
887   EXPECT_EQ(0,
888             mock_ime_engine_handler_->set_surrounding_text_call_count());
889
890   // Should not be called twice with same condition.
891   ime_->OnCaretBoundsChanged(this);
892   EXPECT_EQ(0,
893             mock_ime_engine_handler_->set_surrounding_text_call_count());
894 }
895
896 class InputMethodChromeOSKeyEventTest : public InputMethodChromeOSTest {
897  public:
898   InputMethodChromeOSKeyEventTest() {}
899   virtual ~InputMethodChromeOSKeyEventTest() {}
900
901   virtual void SetUp() OVERRIDE {
902     InputMethodChromeOSTest::SetUp();
903     ime_->Init(true);
904   }
905
906   DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSKeyEventTest);
907 };
908
909 TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseTest) {
910   const int kFlags = ui::EF_SHIFT_DOWN;
911   ScopedXI2Event xevent;
912   xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, kFlags);
913   const ui::KeyEvent event(xevent, true);
914
915   // Do key event.
916   input_type_ = TEXT_INPUT_TYPE_TEXT;
917   ime_->OnTextInputTypeChanged(this);
918   ime_->DispatchKeyEvent(event);
919
920   // Check before state.
921   const ui::KeyEvent* key_event =
922       mock_ime_engine_handler_->last_processed_key_event();
923   EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count());
924   EXPECT_EQ(ui::VKEY_A, key_event->key_code());
925   EXPECT_EQ("KeyA", key_event->code());
926   EXPECT_EQ(kFlags, key_event->flags());
927   EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
928
929   // Do callback.
930   mock_ime_engine_handler_->last_passed_callback().Run(true);
931
932   // Check the results
933   EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
934   const ui::KeyEvent* stored_event =
935       ime_->process_key_event_post_ime_args().event;
936   EXPECT_TRUE(stored_event->HasNativeEvent());
937   EXPECT_TRUE(IsEqualXKeyEvent(*xevent, *(stored_event->native_event())));
938   EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
939 }
940
941 TEST_F(InputMethodChromeOSKeyEventTest, MultiKeyEventDelayResponseTest) {
942   // Preparation
943   input_type_ = TEXT_INPUT_TYPE_TEXT;
944   ime_->OnTextInputTypeChanged(this);
945
946   const int kFlags = ui::EF_SHIFT_DOWN;
947   ScopedXI2Event xevent;
948   xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_B, kFlags);
949   const ui::KeyEvent event(xevent, true);
950
951   // Do key event.
952   ime_->DispatchKeyEvent(event);
953   const ui::KeyEvent* key_event =
954       mock_ime_engine_handler_->last_processed_key_event();
955   EXPECT_EQ(ui::VKEY_B, key_event->key_code());
956   EXPECT_EQ("KeyB", key_event->code());
957   EXPECT_EQ(kFlags, key_event->flags());
958
959   KeyEventCallback first_callback =
960       mock_ime_engine_handler_->last_passed_callback();
961
962   // Do key event again.
963   ScopedXI2Event xevent2;
964   xevent2.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_C, kFlags);
965   const ui::KeyEvent event2(xevent2, true);
966
967   ime_->DispatchKeyEvent(event2);
968   const ui::KeyEvent* key_event2 =
969       mock_ime_engine_handler_->last_processed_key_event();
970   EXPECT_EQ(ui::VKEY_C, key_event2->key_code());
971   EXPECT_EQ("KeyC", key_event2->code());
972   EXPECT_EQ(kFlags, key_event2->flags());
973
974   // Check before state.
975   EXPECT_EQ(2,
976             mock_ime_engine_handler_->process_key_event_call_count());
977   EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
978
979   // Do callback for first key event.
980   first_callback.Run(true);
981
982   // Check the results for first key event.
983   EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
984   const ui::KeyEvent* stored_event =
985       ime_->process_key_event_post_ime_args().event;
986   EXPECT_TRUE(stored_event->HasNativeEvent());
987   EXPECT_TRUE(IsEqualXKeyEvent(*xevent, *(stored_event->native_event())));
988   EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
989
990   // Do callback for second key event.
991   mock_ime_engine_handler_->last_passed_callback().Run(false);
992
993   // Check the results for second key event.
994   EXPECT_EQ(2, ime_->process_key_event_post_ime_call_count());
995   stored_event = ime_->process_key_event_post_ime_args().event;
996   EXPECT_TRUE(stored_event->HasNativeEvent());
997   EXPECT_TRUE(IsEqualXKeyEvent(*xevent2, *(stored_event->native_event())));
998   EXPECT_FALSE(ime_->process_key_event_post_ime_args().handled);
999 }
1000
1001 TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseResetTest) {
1002   ScopedXI2Event xevent;
1003   xevent.InitKeyEvent(ui::ET_KEY_PRESSED, ui::VKEY_A, ui::EF_SHIFT_DOWN);
1004   const ui::KeyEvent event(xevent, true);
1005
1006   // Do key event.
1007   input_type_ = TEXT_INPUT_TYPE_TEXT;
1008   ime_->OnTextInputTypeChanged(this);
1009   ime_->DispatchKeyEvent(event);
1010
1011   // Check before state.
1012   EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count());
1013   EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1014
1015   ime_->ResetContext();
1016
1017   // Do callback.
1018   mock_ime_engine_handler_->last_passed_callback().Run(true);
1019
1020   EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
1021 }
1022 // TODO(nona): Introduce ProcessKeyEventPostIME tests(crbug.com/156593).
1023
1024 }  // namespace ui