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