Add ICU message format support
[chromium-blink-merge.git] / ui / base / ime / input_method_chromeos_unittest.cc
blobc1dcdf21c2bc43ad9b9b57874291db8744b36c9d
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ui/base/ime/input_method_chromeos.h"
7 #include <X11/Xlib.h>
8 #undef Bool
9 #undef FocusIn
10 #undef FocusOut
11 #undef None
13 #include <cstring>
15 #include "base/i18n/char_iterator.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "ui/base/ime/chromeos/composition_text_chromeos.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/dummy_text_input_client.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/event_utils.h"
28 #include "ui/events/keycodes/dom/dom_code.h"
29 #include "ui/events/keycodes/dom/keycode_converter.h"
30 #include "ui/events/test/events_test_utils_x11.h"
31 #include "ui/gfx/geometry/rect.h"
33 using base::UTF8ToUTF16;
34 using base::UTF16ToUTF8;
36 namespace ui {
37 namespace {
39 const base::string16 kSampleText = base::UTF8ToUTF16(
40 "\xE3\x81\x82\xE3\x81\x84\xE3\x81\x86\xE3\x81\x88\xE3\x81\x8A");
42 typedef chromeos::IMEEngineHandlerInterface::KeyEventDoneCallback
43 KeyEventCallback;
45 uint32 GetOffsetInUTF16(
46 const base::string16& utf16_string, uint32 utf8_offset) {
47 DCHECK_LT(utf8_offset, utf16_string.size());
48 base::i18n::UTF16CharIterator char_iterator(&utf16_string);
49 for (size_t i = 0; i < utf8_offset; ++i)
50 char_iterator.Advance();
51 return char_iterator.array_pos();
54 enum KeyEventHandlerBehavior {
55 KEYEVENT_CONSUME,
56 KEYEVENT_NOT_CONSUME,
59 } // namespace
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) {
69 struct ProcessKeyEventPostIMEArgs {
70 ProcessKeyEventPostIMEArgs() : event(NULL), handled(false) {}
71 const ui::KeyEvent* event;
72 bool handled;
75 // Overridden from InputMethodChromeOS:
76 void ProcessKeyEventPostIME(ui::KeyEvent* key_event,
77 bool handled) override {
78 InputMethodChromeOS::ProcessKeyEventPostIME(key_event, handled);
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_;
84 void ResetCallCount() {
85 process_key_event_post_ime_call_count_ = 0;
88 const ProcessKeyEventPostIMEArgs& process_key_event_post_ime_args() const {
89 return process_key_event_post_ime_args_;
92 int process_key_event_post_ime_call_count() const {
93 return process_key_event_post_ime_call_count_;
96 // Change access rights for testing.
97 using InputMethodChromeOS::ExtractCompositionText;
98 using InputMethodChromeOS::ResetContext;
100 private:
101 ProcessKeyEventPostIMEArgs process_key_event_post_ime_args_;
102 int process_key_event_post_ime_call_count_;
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) {}
116 virtual ~SynchronousKeyEventHandler() {}
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);
128 private:
129 const uint32 expected_keyval_;
130 const uint32 expected_keycode_;
131 const uint32 expected_state_;
132 const KeyEventHandlerBehavior behavior_;
134 DISALLOW_COPY_AND_ASSIGN(SynchronousKeyEventHandler);
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) {}
146 virtual ~AsynchronousKeyEventHandler() {}
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;
158 void RunCallback(KeyEventHandlerBehavior behavior) {
159 callback_.Run(behavior == KEYEVENT_CONSUME);
162 private:
163 const uint32 expected_keyval_;
164 const uint32 expected_keycode_;
165 const uint32 expected_state_;
166 KeyEventCallback callback_;
168 DISALLOW_COPY_AND_ASSIGN(AsynchronousKeyEventHandler);
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) {}
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);
188 private:
189 const std::string expected_surrounding_text_;
190 const uint32 expected_cursor_position_;
191 const uint32 expected_anchor_position_;
193 DISALLOW_COPY_AND_ASSIGN(SetSurroundingTextVerifier);
196 class InputMethodChromeOSTest : public internal::InputMethodDelegate,
197 public testing::Test,
198 public DummyTextInputClient {
199 public:
200 InputMethodChromeOSTest()
201 : dispatched_key_event_(ui::ET_UNKNOWN, ui::VKEY_UNKNOWN, ui::EF_NONE),
202 stop_propagation_post_ime_(false) {
203 ResetFlags();
206 ~InputMethodChromeOSTest() override {}
208 void SetUp() override {
209 chromeos::IMEBridge::Initialize();
211 mock_ime_engine_handler_.reset(
212 new chromeos::MockIMEEngineHandler());
213 chromeos::IMEBridge::Get()->SetCurrentEngineHandler(
214 mock_ime_engine_handler_.get());
216 mock_ime_candidate_window_handler_.reset(
217 new chromeos::MockIMECandidateWindowHandler());
218 chromeos::IMEBridge::Get()->SetCandidateWindowHandler(
219 mock_ime_candidate_window_handler_.get());
221 ime_.reset(new TestableInputMethodChromeOS(this));
222 ime_->SetFocusedTextInputClient(this);
225 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();
235 ResetFlags();
238 // Overridden from ui::internal::InputMethodDelegate:
239 ui::EventDispatchDetails DispatchKeyEventPostIME(
240 ui::KeyEvent* event) override {
241 dispatched_key_event_ = *event;
242 if (stop_propagation_post_ime_)
243 event->StopPropagation();
244 return ui::EventDispatchDetails();
247 // Overridden from ui::TextInputClient:
248 void SetCompositionText(const CompositionText& composition) override {
249 composition_text_ = composition;
251 void ConfirmCompositionText() override {
252 confirmed_text_ = composition_text_;
253 composition_text_.Clear();
255 void ClearCompositionText() override { composition_text_.Clear(); }
256 void InsertText(const base::string16& text) override {
257 inserted_text_ = text;
259 void InsertChar(base::char16 ch, int flags) override {
260 inserted_char_ = ch;
261 inserted_char_flags_ = flags;
263 TextInputType GetTextInputType() const override { return input_type_; }
264 TextInputMode GetTextInputMode() const override { return input_mode_; }
265 bool CanComposeInline() const override { return can_compose_inline_; }
266 gfx::Rect GetCaretBounds() const override { return caret_bounds_; }
267 bool HasCompositionText() const override {
268 CompositionText empty;
269 return composition_text_ != empty;
271 bool GetTextRange(gfx::Range* range) const override {
272 *range = text_range_;
273 return true;
275 bool GetSelectionRange(gfx::Range* range) const override {
276 *range = selection_range_;
277 return true;
279 bool GetTextFromRange(const gfx::Range& range,
280 base::string16* text) const override {
281 *text = surrounding_text_.substr(range.GetMin(), range.length());
282 return true;
284 void OnInputMethodChanged() override {
285 ++on_input_method_changed_call_count_;
288 bool HasNativeEvent() const {
289 return dispatched_key_event_.HasNativeEvent();
292 void ResetFlags() {
293 dispatched_key_event_ = ui::KeyEvent(ui::ET_UNKNOWN, ui::VKEY_UNKNOWN,
294 ui::EF_NONE);
296 composition_text_.Clear();
297 confirmed_text_.Clear();
298 inserted_text_.clear();
299 inserted_char_ = 0;
300 inserted_char_flags_ = 0;
301 on_input_method_changed_call_count_ = 0;
303 input_type_ = TEXT_INPUT_TYPE_NONE;
304 input_mode_ = TEXT_INPUT_MODE_DEFAULT;
305 can_compose_inline_ = true;
306 caret_bounds_ = gfx::Rect();
309 scoped_ptr<TestableInputMethodChromeOS> ime_;
311 // Copy of the dispatched key event.
312 ui::KeyEvent dispatched_key_event_;
314 // Variables for remembering the parameters that are passed to
315 // ui::TextInputClient functions.
316 CompositionText composition_text_;
317 CompositionText confirmed_text_;
318 base::string16 inserted_text_;
319 base::char16 inserted_char_;
320 unsigned int on_input_method_changed_call_count_;
321 int inserted_char_flags_;
323 // Variables that will be returned from the ui::TextInputClient functions.
324 TextInputType input_type_;
325 TextInputMode input_mode_;
326 bool can_compose_inline_;
327 gfx::Rect caret_bounds_;
328 gfx::Range text_range_;
329 gfx::Range selection_range_;
330 base::string16 surrounding_text_;
332 scoped_ptr<chromeos::MockIMEEngineHandler> mock_ime_engine_handler_;
333 scoped_ptr<chromeos::MockIMECandidateWindowHandler>
334 mock_ime_candidate_window_handler_;
336 bool stop_propagation_post_ime_;
338 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSTest);
341 // Tests public APIs in ui::InputMethod first.
343 TEST_F(InputMethodChromeOSTest, GetInputLocale) {
344 // ui::InputMethodChromeOS does not support the API.
345 ime_->OnFocus();
346 EXPECT_EQ("", ime_->GetInputLocale());
349 TEST_F(InputMethodChromeOSTest, GetInputTextType) {
350 ime_->OnFocus();
351 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
352 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
353 ime_->OnTextInputTypeChanged(this);
354 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
355 input_type_ = TEXT_INPUT_TYPE_TEXT;
356 ime_->OnTextInputTypeChanged(this);
357 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT, ime_->GetTextInputType());
360 TEST_F(InputMethodChromeOSTest, CanComposeInline) {
361 ime_->OnFocus();
362 EXPECT_TRUE(ime_->CanComposeInline());
363 can_compose_inline_ = false;
364 ime_->OnTextInputTypeChanged(this);
365 EXPECT_FALSE(ime_->CanComposeInline());
368 TEST_F(InputMethodChromeOSTest, GetTextInputClient) {
369 ime_->OnFocus();
370 EXPECT_EQ(this, ime_->GetTextInputClient());
371 ime_->SetFocusedTextInputClient(NULL);
372 EXPECT_EQ(NULL, ime_->GetTextInputClient());
375 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedClient) {
376 ime_->OnFocus();
377 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
378 ime_->SetFocusedTextInputClient(NULL);
379 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
380 ime_->OnTextInputTypeChanged(this);
381 // The OnTextInputTypeChanged() call above should be ignored since |this| is
382 // not the current focused client.
383 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
385 ime_->SetFocusedTextInputClient(this);
386 ime_->OnTextInputTypeChanged(this);
387 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
390 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow) {
391 ime_->OnFocus();
392 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
393 ime_->OnBlur();
394 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
395 ime_->OnTextInputTypeChanged(this);
396 // The OnTextInputTypeChanged() call above should be ignored since the top-
397 // level window which the ime_ is attached to is not currently focused.
398 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
400 ime_->OnFocus();
401 ime_->OnTextInputTypeChanged(this);
402 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
405 TEST_F(InputMethodChromeOSTest, GetInputTextType_WithoutFocusedWindow2) {
406 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
407 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
408 ime_->OnTextInputTypeChanged(this);
409 EXPECT_EQ(TEXT_INPUT_TYPE_NONE, ime_->GetTextInputType());
411 ime_->OnFocus();
412 ime_->OnTextInputTypeChanged(this);
413 EXPECT_EQ(TEXT_INPUT_TYPE_PASSWORD, ime_->GetTextInputType());
416 // Confirm that IBusClient::FocusIn is called on "connected" if input_type_ is
417 // TEXT.
418 TEST_F(InputMethodChromeOSTest, FocusIn_Text) {
419 ime_->OnFocus();
420 // A context shouldn't be created since the daemon is not running.
421 EXPECT_EQ(0U, on_input_method_changed_call_count_);
422 // Click a text input form.
423 input_type_ = TEXT_INPUT_TYPE_TEXT;
424 ime_->OnTextInputTypeChanged(this);
425 // Since a form has focus, IBusClient::FocusIn() should be called.
426 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
427 EXPECT_EQ(
429 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
430 // ui::TextInputClient::OnInputMethodChanged() should be called when
431 // ui::InputMethodChromeOS connects/disconnects to/from ibus-daemon and the
432 // current text input type is not NONE.
433 EXPECT_EQ(1U, on_input_method_changed_call_count_);
436 // Confirm that InputMethodEngine::FocusIn is called on "connected" even if
437 // input_type_ is PASSWORD.
438 TEST_F(InputMethodChromeOSTest, FocusIn_Password) {
439 ime_->OnFocus();
440 EXPECT_EQ(0U, on_input_method_changed_call_count_);
441 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
442 ime_->OnTextInputTypeChanged(this);
443 // InputMethodEngine::FocusIn() should be called even for password field.
444 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
445 EXPECT_EQ(1U, on_input_method_changed_call_count_);
448 // Confirm that IBusClient::FocusOut is called as expected.
449 TEST_F(InputMethodChromeOSTest, FocusOut_None) {
450 input_type_ = TEXT_INPUT_TYPE_TEXT;
451 ime_->OnFocus();
452 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
453 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
454 input_type_ = TEXT_INPUT_TYPE_NONE;
455 ime_->OnTextInputTypeChanged(this);
456 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
457 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
460 // Confirm that IBusClient::FocusOut is called as expected.
461 TEST_F(InputMethodChromeOSTest, FocusOut_Password) {
462 input_type_ = TEXT_INPUT_TYPE_TEXT;
463 ime_->OnFocus();
464 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
465 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
466 input_type_ = TEXT_INPUT_TYPE_PASSWORD;
467 ime_->OnTextInputTypeChanged(this);
468 EXPECT_EQ(2, mock_ime_engine_handler_->focus_in_call_count());
469 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
472 // FocusIn/FocusOut scenario test
473 TEST_F(InputMethodChromeOSTest, Focus_Scenario) {
474 ime_->OnFocus();
475 // Confirm that both FocusIn and FocusOut are NOT called.
476 EXPECT_EQ(0, mock_ime_engine_handler_->focus_in_call_count());
477 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
478 EXPECT_EQ(TEXT_INPUT_TYPE_NONE,
479 mock_ime_engine_handler_->last_text_input_context().type);
480 EXPECT_EQ(TEXT_INPUT_MODE_DEFAULT,
481 mock_ime_engine_handler_->last_text_input_context().mode);
483 input_type_ = TEXT_INPUT_TYPE_TEXT;
484 input_mode_ = TEXT_INPUT_MODE_LATIN;
485 ime_->OnTextInputTypeChanged(this);
486 // Confirm that only FocusIn is called, the TextInputType is TEXT and the
487 // TextInputMode is LATIN..
488 EXPECT_EQ(1, mock_ime_engine_handler_->focus_in_call_count());
489 EXPECT_EQ(0, mock_ime_engine_handler_->focus_out_call_count());
490 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT,
491 mock_ime_engine_handler_->last_text_input_context().type);
492 EXPECT_EQ(TEXT_INPUT_MODE_LATIN,
493 mock_ime_engine_handler_->last_text_input_context().mode);
495 input_mode_ = TEXT_INPUT_MODE_KANA;
496 ime_->OnTextInputTypeChanged(this);
497 // Confirm that both FocusIn and FocusOut are called for mode change.
498 EXPECT_EQ(2, mock_ime_engine_handler_->focus_in_call_count());
499 EXPECT_EQ(1, mock_ime_engine_handler_->focus_out_call_count());
500 EXPECT_EQ(TEXT_INPUT_TYPE_TEXT,
501 mock_ime_engine_handler_->last_text_input_context().type);
502 EXPECT_EQ(TEXT_INPUT_MODE_KANA,
503 mock_ime_engine_handler_->last_text_input_context().mode);
505 input_type_ = TEXT_INPUT_TYPE_URL;
506 ime_->OnTextInputTypeChanged(this);
507 // Confirm that both FocusIn and FocusOut are called and the TextInputType is
508 // changed to URL.
509 EXPECT_EQ(3, mock_ime_engine_handler_->focus_in_call_count());
510 EXPECT_EQ(2, mock_ime_engine_handler_->focus_out_call_count());
511 EXPECT_EQ(TEXT_INPUT_TYPE_URL,
512 mock_ime_engine_handler_->last_text_input_context().type);
513 EXPECT_EQ(TEXT_INPUT_MODE_KANA,
514 mock_ime_engine_handler_->last_text_input_context().mode);
516 // Confirm that FocusOut is called when set focus to NULL client.
517 ime_->SetFocusedTextInputClient(NULL);
518 EXPECT_EQ(3, mock_ime_engine_handler_->focus_in_call_count());
519 EXPECT_EQ(3, mock_ime_engine_handler_->focus_out_call_count());
520 // Confirm that FocusIn is called when set focus to this client.
521 ime_->SetFocusedTextInputClient(this);
522 EXPECT_EQ(4, mock_ime_engine_handler_->focus_in_call_count());
523 EXPECT_EQ(3, mock_ime_engine_handler_->focus_out_call_count());
526 // Test if the new |caret_bounds_| is correctly sent to ibus-daemon.
527 TEST_F(InputMethodChromeOSTest, OnCaretBoundsChanged) {
528 input_type_ = TEXT_INPUT_TYPE_TEXT;
529 ime_->OnFocus();
530 EXPECT_EQ(
532 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
533 caret_bounds_ = gfx::Rect(1, 2, 3, 4);
534 ime_->OnCaretBoundsChanged(this);
535 EXPECT_EQ(
537 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
538 caret_bounds_ = gfx::Rect(0, 2, 3, 4);
539 ime_->OnCaretBoundsChanged(this);
540 EXPECT_EQ(
542 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
543 caret_bounds_ = gfx::Rect(0, 2, 3, 4); // unchanged
544 ime_->OnCaretBoundsChanged(this);
545 // Current InputMethodChromeOS implementation performs the IPC
546 // regardless of the bounds are changed or not.
547 EXPECT_EQ(
549 mock_ime_candidate_window_handler_->set_cursor_bounds_call_count());
552 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_NoAttribute) {
553 const base::string16 kSampleAsciiText = UTF8ToUTF16("Sample Text");
554 const uint32 kCursorPos = 2UL;
556 chromeos::CompositionText chromeos_composition_text;
557 chromeos_composition_text.set_text(kSampleAsciiText);
559 CompositionText composition_text;
560 ime_->ExtractCompositionText(
561 chromeos_composition_text, kCursorPos, &composition_text);
562 EXPECT_EQ(kSampleAsciiText, composition_text.text);
563 // If there is no selection, |selection| represents cursor position.
564 EXPECT_EQ(kCursorPos, composition_text.selection.start());
565 EXPECT_EQ(kCursorPos, composition_text.selection.end());
566 // If there is no underline, |underlines| contains one underline and it is
567 // whole text underline.
568 ASSERT_EQ(1UL, composition_text.underlines.size());
569 EXPECT_EQ(0UL, composition_text.underlines[0].start_offset);
570 EXPECT_EQ(kSampleAsciiText.size(), composition_text.underlines[0].end_offset);
571 EXPECT_FALSE(composition_text.underlines[0].thick);
574 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_SingleUnderline) {
575 const uint32 kCursorPos = 2UL;
577 // Set up chromeos composition text with one underline attribute.
578 chromeos::CompositionText chromeos_composition_text;
579 chromeos_composition_text.set_text(kSampleText);
580 chromeos::CompositionText::UnderlineAttribute underline;
581 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_SINGLE;
582 underline.start_index = 1UL;
583 underline.end_index = 4UL;
584 chromeos_composition_text.mutable_underline_attributes()->push_back(
585 underline);
587 CompositionText composition_text;
588 ime_->ExtractCompositionText(
589 chromeos_composition_text, kCursorPos, &composition_text);
590 EXPECT_EQ(kSampleText, composition_text.text);
591 // If there is no selection, |selection| represents cursor position.
592 EXPECT_EQ(kCursorPos, composition_text.selection.start());
593 EXPECT_EQ(kCursorPos, composition_text.selection.end());
594 ASSERT_EQ(1UL, composition_text.underlines.size());
595 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
596 composition_text.underlines[0].start_offset);
597 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
598 composition_text.underlines[0].end_offset);
599 // Single underline represents as black thin line.
600 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
601 EXPECT_FALSE(composition_text.underlines[0].thick);
602 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
603 composition_text.underlines[0].background_color);
606 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_DoubleUnderline) {
607 const uint32 kCursorPos = 2UL;
609 // Set up chromeos composition text with one underline attribute.
610 chromeos::CompositionText chromeos_composition_text;
611 chromeos_composition_text.set_text(kSampleText);
612 chromeos::CompositionText::UnderlineAttribute underline;
613 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_DOUBLE;
614 underline.start_index = 1UL;
615 underline.end_index = 4UL;
616 chromeos_composition_text.mutable_underline_attributes()->push_back(
617 underline);
619 CompositionText composition_text;
620 ime_->ExtractCompositionText(
621 chromeos_composition_text, kCursorPos, &composition_text);
622 EXPECT_EQ(kSampleText, composition_text.text);
623 // If there is no selection, |selection| represents cursor position.
624 EXPECT_EQ(kCursorPos, composition_text.selection.start());
625 EXPECT_EQ(kCursorPos, composition_text.selection.end());
626 ASSERT_EQ(1UL, composition_text.underlines.size());
627 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
628 composition_text.underlines[0].start_offset);
629 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
630 composition_text.underlines[0].end_offset);
631 // Double underline represents as black thick line.
632 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
633 EXPECT_TRUE(composition_text.underlines[0].thick);
634 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
635 composition_text.underlines[0].background_color);
638 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_ErrorUnderline) {
639 const uint32 kCursorPos = 2UL;
641 // Set up chromeos composition text with one underline attribute.
642 chromeos::CompositionText chromeos_composition_text;
643 chromeos_composition_text.set_text(kSampleText);
644 chromeos::CompositionText::UnderlineAttribute underline;
645 underline.type = chromeos::CompositionText::COMPOSITION_TEXT_UNDERLINE_ERROR;
646 underline.start_index = 1UL;
647 underline.end_index = 4UL;
648 chromeos_composition_text.mutable_underline_attributes()->push_back(
649 underline);
651 CompositionText composition_text;
652 ime_->ExtractCompositionText(
653 chromeos_composition_text, kCursorPos, &composition_text);
654 EXPECT_EQ(kSampleText, composition_text.text);
655 EXPECT_EQ(kCursorPos, composition_text.selection.start());
656 EXPECT_EQ(kCursorPos, composition_text.selection.end());
657 ASSERT_EQ(1UL, composition_text.underlines.size());
658 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.start_index),
659 composition_text.underlines[0].start_offset);
660 EXPECT_EQ(GetOffsetInUTF16(kSampleText, underline.end_index),
661 composition_text.underlines[0].end_offset);
662 // Error underline represents as red thin line.
663 EXPECT_EQ(SK_ColorRED, composition_text.underlines[0].color);
664 EXPECT_FALSE(composition_text.underlines[0].thick);
667 TEST_F(InputMethodChromeOSTest, ExtractCompositionTextTest_Selection) {
668 const uint32 kCursorPos = 2UL;
670 // Set up chromeos composition text with one underline attribute.
671 chromeos::CompositionText chromeos_composition_text;
672 chromeos_composition_text.set_text(kSampleText);
673 chromeos_composition_text.set_selection_start(1UL);
674 chromeos_composition_text.set_selection_end(4UL);
676 CompositionText composition_text;
677 ime_->ExtractCompositionText(
678 chromeos_composition_text, kCursorPos, &composition_text);
679 EXPECT_EQ(kSampleText, composition_text.text);
680 EXPECT_EQ(kCursorPos, composition_text.selection.start());
681 EXPECT_EQ(kCursorPos, composition_text.selection.end());
682 ASSERT_EQ(1UL, composition_text.underlines.size());
683 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
684 chromeos_composition_text.selection_start()),
685 composition_text.underlines[0].start_offset);
686 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
687 chromeos_composition_text.selection_end()),
688 composition_text.underlines[0].end_offset);
689 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
690 EXPECT_TRUE(composition_text.underlines[0].thick);
691 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
692 composition_text.underlines[0].background_color);
695 TEST_F(InputMethodChromeOSTest,
696 ExtractCompositionTextTest_SelectionStartWithCursor) {
697 const uint32 kCursorPos = 1UL;
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(kCursorPos);
703 chromeos_composition_text.set_selection_end(4UL);
705 CompositionText composition_text;
706 ime_->ExtractCompositionText(
707 chromeos_composition_text, kCursorPos, &composition_text);
708 EXPECT_EQ(kSampleText, composition_text.text);
709 // If the cursor position is same as selection bounds, selection start
710 // position become opposit side of selection from cursor.
711 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
712 chromeos_composition_text.selection_end()),
713 composition_text.selection.start());
714 EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
715 composition_text.selection.end());
716 ASSERT_EQ(1UL, composition_text.underlines.size());
717 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
718 chromeos_composition_text.selection_start()),
719 composition_text.underlines[0].start_offset);
720 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
721 chromeos_composition_text.selection_end()),
722 composition_text.underlines[0].end_offset);
723 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
724 EXPECT_TRUE(composition_text.underlines[0].thick);
725 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
726 composition_text.underlines[0].background_color);
729 TEST_F(InputMethodChromeOSTest,
730 ExtractCompositionTextTest_SelectionEndWithCursor) {
731 const uint32 kCursorPos = 4UL;
733 // Set up chromeos composition text with one underline attribute.
734 chromeos::CompositionText chromeos_composition_text;
735 chromeos_composition_text.set_text(kSampleText);
736 chromeos_composition_text.set_selection_start(1UL);
737 chromeos_composition_text.set_selection_end(kCursorPos);
739 CompositionText composition_text;
740 ime_->ExtractCompositionText(
741 chromeos_composition_text, kCursorPos, &composition_text);
742 EXPECT_EQ(kSampleText, composition_text.text);
743 // If the cursor position is same as selection bounds, selection start
744 // position become opposit side of selection from cursor.
745 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
746 chromeos_composition_text.selection_start()),
747 composition_text.selection.start());
748 EXPECT_EQ(GetOffsetInUTF16(kSampleText, kCursorPos),
749 composition_text.selection.end());
750 ASSERT_EQ(1UL, composition_text.underlines.size());
751 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
752 chromeos_composition_text.selection_start()),
753 composition_text.underlines[0].start_offset);
754 EXPECT_EQ(GetOffsetInUTF16(kSampleText,
755 chromeos_composition_text.selection_end()),
756 composition_text.underlines[0].end_offset);
757 EXPECT_EQ(SK_ColorBLACK, composition_text.underlines[0].color);
758 EXPECT_TRUE(composition_text.underlines[0].thick);
759 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
760 composition_text.underlines[0].background_color);
763 TEST_F(InputMethodChromeOSTest, SurroundingText_NoSelectionTest) {
764 ime_->OnFocus();
765 // Click a text input form.
766 input_type_ = TEXT_INPUT_TYPE_TEXT;
767 ime_->OnTextInputTypeChanged(this);
769 // Set the TextInputClient behaviors.
770 surrounding_text_ = UTF8ToUTF16("abcdef");
771 text_range_ = gfx::Range(0, 6);
772 selection_range_ = gfx::Range(3, 3);
774 // Set the verifier for SetSurroundingText mock call.
775 SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 3, 3);
778 ime_->OnCaretBoundsChanged(this);
780 // Check the call count.
781 EXPECT_EQ(1,
782 mock_ime_engine_handler_->set_surrounding_text_call_count());
783 EXPECT_EQ(UTF16ToUTF8(surrounding_text_),
784 mock_ime_engine_handler_->last_set_surrounding_text());
785 EXPECT_EQ(3U,
786 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
787 EXPECT_EQ(3U,
788 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
791 TEST_F(InputMethodChromeOSTest, SurroundingText_SelectionTest) {
792 ime_->OnFocus();
793 // Click a text input form.
794 input_type_ = TEXT_INPUT_TYPE_TEXT;
795 ime_->OnTextInputTypeChanged(this);
797 // Set the TextInputClient behaviors.
798 surrounding_text_ = UTF8ToUTF16("abcdef");
799 text_range_ = gfx::Range(0, 6);
800 selection_range_ = gfx::Range(2, 5);
802 // Set the verifier for SetSurroundingText mock call.
803 SetSurroundingTextVerifier verifier(UTF16ToUTF8(surrounding_text_), 2, 5);
805 ime_->OnCaretBoundsChanged(this);
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(2U,
813 mock_ime_engine_handler_->last_set_surrounding_cursor_pos());
814 EXPECT_EQ(5U,
815 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
818 TEST_F(InputMethodChromeOSTest, SurroundingText_PartialText) {
819 ime_->OnFocus();
820 // Click a text input form.
821 input_type_ = TEXT_INPUT_TYPE_TEXT;
822 ime_->OnTextInputTypeChanged(this);
824 // Set the TextInputClient behaviors.
825 surrounding_text_ = UTF8ToUTF16("abcdefghij");
826 text_range_ = gfx::Range(5, 10);
827 selection_range_ = gfx::Range(7, 9);
829 ime_->OnCaretBoundsChanged(this);
831 // Check the call count.
832 EXPECT_EQ(1,
833 mock_ime_engine_handler_->set_surrounding_text_call_count());
834 // Set the verifier for SetSurroundingText mock call.
835 // Here (2, 4) is selection range in expected surrounding text coordinates.
836 EXPECT_EQ("fghij",
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(4U,
841 mock_ime_engine_handler_->last_set_surrounding_anchor_pos());
844 TEST_F(InputMethodChromeOSTest, SurroundingText_BecomeEmptyText) {
845 ime_->OnFocus();
846 // Click a text input form.
847 input_type_ = TEXT_INPUT_TYPE_TEXT;
848 ime_->OnTextInputTypeChanged(this);
850 // Set the TextInputClient behaviors.
851 // If the surrounding text becomes empty, text_range become (0, 0) and
852 // selection range become invalid.
853 surrounding_text_ = UTF8ToUTF16("");
854 text_range_ = gfx::Range(0, 0);
855 selection_range_ = gfx::Range::InvalidRange();
857 ime_->OnCaretBoundsChanged(this);
859 // Check the call count.
860 EXPECT_EQ(0,
861 mock_ime_engine_handler_->set_surrounding_text_call_count());
863 // Should not be called twice with same condition.
864 ime_->OnCaretBoundsChanged(this);
865 EXPECT_EQ(0,
866 mock_ime_engine_handler_->set_surrounding_text_call_count());
869 class InputMethodChromeOSKeyEventTest : public InputMethodChromeOSTest {
870 public:
871 InputMethodChromeOSKeyEventTest() {}
872 ~InputMethodChromeOSKeyEventTest() override {}
874 void SetUp() override {
875 InputMethodChromeOSTest::SetUp();
876 ime_->OnFocus();
879 DISALLOW_COPY_AND_ASSIGN(InputMethodChromeOSKeyEventTest);
882 TEST_F(InputMethodChromeOSKeyEventTest, KeyEventDelayResponseTest) {
883 const int kFlags = ui::EF_SHIFT_DOWN;
884 ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_A, kFlags);
886 // Do key event.
887 input_type_ = TEXT_INPUT_TYPE_TEXT;
888 ime_->OnTextInputTypeChanged(this);
889 ime_->DispatchKeyEvent(&event);
891 // Check before state.
892 const ui::KeyEvent* key_event =
893 mock_ime_engine_handler_->last_processed_key_event();
894 EXPECT_EQ(1, mock_ime_engine_handler_->process_key_event_call_count());
895 EXPECT_EQ(ui::VKEY_A, key_event->key_code());
896 EXPECT_EQ(kFlags, key_event->flags());
897 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
899 (static_cast<chromeos::IMEInputContextHandlerInterface*>(ime_.get()))
900 ->CommitText("A");
902 EXPECT_EQ(0, inserted_char_);
904 // Do callback.
905 mock_ime_engine_handler_->last_passed_callback().Run(true);
907 // Check the results
908 EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
909 const ui::KeyEvent* stored_event =
910 ime_->process_key_event_post_ime_args().event;
911 EXPECT_EQ(ui::VKEY_A, stored_event->key_code());
912 EXPECT_EQ(kFlags, stored_event->flags());
913 EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
915 EXPECT_EQ(L'A', inserted_char_);
918 TEST_F(InputMethodChromeOSKeyEventTest, MultiKeyEventDelayResponseTest) {
919 // Preparation
920 input_type_ = TEXT_INPUT_TYPE_TEXT;
921 ime_->OnTextInputTypeChanged(this);
923 const int kFlags = ui::EF_SHIFT_DOWN;
924 ui::KeyEvent event(ui::ET_KEY_PRESSED, ui::VKEY_B, kFlags);
926 // Do key event.
927 ime_->DispatchKeyEvent(&event);
928 const ui::KeyEvent* key_event =
929 mock_ime_engine_handler_->last_processed_key_event();
930 EXPECT_EQ(ui::VKEY_B, key_event->key_code());
931 EXPECT_EQ(kFlags, key_event->flags());
933 KeyEventCallback first_callback =
934 mock_ime_engine_handler_->last_passed_callback();
936 // Do key event again.
937 ui::KeyEvent event2(ui::ET_KEY_PRESSED, ui::VKEY_C, kFlags);
939 ime_->DispatchKeyEvent(&event2);
940 const ui::KeyEvent* key_event2 =
941 mock_ime_engine_handler_->last_processed_key_event();
942 EXPECT_EQ(ui::VKEY_C, key_event2->key_code());
943 EXPECT_EQ(kFlags, key_event2->flags());
945 // Check before state.
946 EXPECT_EQ(2,
947 mock_ime_engine_handler_->process_key_event_call_count());
948 EXPECT_EQ(0, ime_->process_key_event_post_ime_call_count());
950 chromeos::CompositionText comp;
951 comp.set_text(base::ASCIIToUTF16("B"));
952 (static_cast<chromeos::IMEInputContextHandlerInterface*>(ime_.get()))
953 ->UpdateCompositionText(comp, comp.text().length(), true);
955 EXPECT_EQ(0, composition_text_.text[0]);
957 // Do callback for first key event.
958 first_callback.Run(true);
960 EXPECT_EQ(comp.text(), composition_text_.text);
962 // Check the results for first key event.
963 EXPECT_EQ(1, ime_->process_key_event_post_ime_call_count());
964 const ui::KeyEvent* stored_event =
965 ime_->process_key_event_post_ime_args().event;
966 EXPECT_EQ(ui::VKEY_B, stored_event->key_code());
967 EXPECT_EQ(kFlags, stored_event->flags());
968 EXPECT_TRUE(ime_->process_key_event_post_ime_args().handled);
969 EXPECT_EQ(0, inserted_char_);
971 // Do callback for second key event.
972 mock_ime_engine_handler_->last_passed_callback().Run(false);
974 // Check the results for second key event.
975 EXPECT_EQ(2, ime_->process_key_event_post_ime_call_count());
976 stored_event = ime_->process_key_event_post_ime_args().event;
977 EXPECT_EQ(ui::VKEY_C, stored_event->key_code());
978 EXPECT_EQ(kFlags, stored_event->flags());
979 EXPECT_FALSE(ime_->process_key_event_post_ime_args().handled);
981 EXPECT_EQ(L'C', inserted_char_);
984 TEST_F(InputMethodChromeOSKeyEventTest, StopPropagationTest) {
985 // Preparation
986 input_type_ = TEXT_INPUT_TYPE_TEXT;
987 ime_->OnTextInputTypeChanged(this);
989 // Do key event with event being stopped propagation.
990 stop_propagation_post_ime_ = true;
991 ui::KeyEvent eventA(ui::ET_KEY_PRESSED, ui::VKEY_A, EF_NONE);
992 eventA.set_character(L'A');
993 ime_->DispatchKeyEvent(&eventA);
994 mock_ime_engine_handler_->last_passed_callback().Run(false);
996 const ui::KeyEvent* key_event =
997 mock_ime_engine_handler_->last_processed_key_event();
998 EXPECT_EQ(ui::VKEY_A, key_event->key_code());
999 EXPECT_EQ(0, inserted_char_);
1001 // Do key event with event not being stopped propagation.
1002 stop_propagation_post_ime_ = false;
1003 ime_->DispatchKeyEvent(&eventA);
1004 mock_ime_engine_handler_->last_passed_callback().Run(false);
1006 key_event = mock_ime_engine_handler_->last_processed_key_event();
1007 EXPECT_EQ(ui::VKEY_A, key_event->key_code());
1008 EXPECT_EQ(L'A', inserted_char_);
1011 } // namespace ui