root/ui/base/ime/remote_input_method_win_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. is_candidate_window_hidden_called_
  2. call_count_set_composition_text
  3. inserted_text
  4. call_count_insert_char
  5. call_count_insert_text
  6. is_candidate_window_shown_called
  7. is_candidate_window_hidden_called
  8. Reset
  9. set_text_input_type
  10. set_text_input_mode
  11. set_caret_bounds
  12. set_composition_character_bounds
  13. set_emulate_pepper_flash
  14. SetCompositionText
  15. InsertChar
  16. InsertText
  17. GetTextInputType
  18. GetTextInputMode
  19. GetCaretBounds
  20. GetCompositionCharacterBounds
  21. HasCompositionText
  22. GetCompositionTextRange
  23. OnCandidateWindowShown
  24. OnCandidateWindowHidden
  25. fabricated_key_events
  26. Reset
  27. DispatchKeyEventPostIME
  28. text_input_client_updated_called_
  29. cancel_composition_called
  30. text_input_client_updated_called
  31. input_scopes
  32. composition_character_bounds
  33. Reset
  34. CancelComposition
  35. OnTextInputClientUpdated
  36. on_input_method_destroyed_changed_
  37. Reset
  38. on_text_input_state_changed
  39. on_input_method_destroyed_changed
  40. OnTextInputTypeChanged
  41. OnFocus
  42. OnBlur
  43. OnCaretBoundsChanged
  44. OnTextInputStateChanged
  45. OnInputMethodDestroyed
  46. OnShowImeIfNeeded
  47. TEST
  48. TEST
  49. TEST
  50. TEST
  51. TEST
  52. TEST
  53. TEST
  54. TEST
  55. TEST
  56. TEST
  57. TEST
  58. TEST
  59. TEST
  60. TEST
  61. TEST
  62. TEST
  63. TEST

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/base/ime/remote_input_method_win.h"

#include <InputScope.h>

#include <vector>

#include "base/memory/scoped_ptr.h"
#include "base/scoped_observer.h"
#include "base/strings/string16.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/ime/composition_text.h"
#include "ui/base/ime/dummy_text_input_client.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/input_method_delegate.h"
#include "ui/base/ime/input_method_observer.h"
#include "ui/base/ime/remote_input_method_delegate_win.h"
#include "ui/events/event.h"

namespace ui {
namespace {

class MockTextInputClient : public DummyTextInputClient {
 public:
  MockTextInputClient()
      : text_input_type_(TEXT_INPUT_TYPE_NONE),
        text_input_mode_(TEXT_INPUT_MODE_DEFAULT),
        call_count_set_composition_text_(0),
        call_count_insert_char_(0),
        call_count_insert_text_(0),
        emulate_pepper_flash_(false),
        is_candidate_window_shown_called_(false),
        is_candidate_window_hidden_called_(false) {
  }

  size_t call_count_set_composition_text() const {
    return call_count_set_composition_text_;
  }
  const base::string16& inserted_text() const {
    return inserted_text_;
  }
  size_t call_count_insert_char() const {
    return call_count_insert_char_;
  }
  size_t call_count_insert_text() const {
    return call_count_insert_text_;
  }
  bool is_candidate_window_shown_called() const {
    return is_candidate_window_shown_called_;
  }
  bool is_candidate_window_hidden_called() const {
    return is_candidate_window_hidden_called_;
  }
  void Reset() {
    text_input_type_ = TEXT_INPUT_TYPE_NONE;
    text_input_mode_ = TEXT_INPUT_MODE_DEFAULT;
    call_count_set_composition_text_ = 0;
    inserted_text_.clear();
    call_count_insert_char_ = 0;
    call_count_insert_text_ = 0;
    caret_bounds_ = gfx::Rect();
    composition_character_bounds_.clear();
    emulate_pepper_flash_ = false;
    is_candidate_window_shown_called_ = false;
    is_candidate_window_hidden_called_ = false;
  }
  void set_text_input_type(ui::TextInputType type) {
    text_input_type_ = type;
  }
  void set_text_input_mode(ui::TextInputMode mode) {
    text_input_mode_ = mode;
  }
  void set_caret_bounds(const gfx::Rect& caret_bounds) {
    caret_bounds_ = caret_bounds;
  }
  void set_composition_character_bounds(
      const std::vector<gfx::Rect>& composition_character_bounds) {
    composition_character_bounds_ = composition_character_bounds;
  }
  void set_emulate_pepper_flash(bool enabled) {
    emulate_pepper_flash_ = enabled;
  }

 private:
  // Overriden from DummyTextInputClient.
  virtual void SetCompositionText(
      const ui::CompositionText& composition) OVERRIDE {
    ++call_count_set_composition_text_;
  }
  virtual void InsertChar(base::char16 ch, int flags) OVERRIDE {
    inserted_text_.append(1, ch);
    ++call_count_insert_char_;
  }
  virtual void InsertText(const base::string16& text) OVERRIDE {
    inserted_text_.append(text);
    ++call_count_insert_text_;
  }
  virtual ui::TextInputType GetTextInputType() const OVERRIDE {
    return text_input_type_;
  }
  virtual ui::TextInputMode GetTextInputMode() const OVERRIDE {
    return text_input_mode_;
  }
  virtual gfx::Rect GetCaretBounds() const {
    return caret_bounds_;
  }
  virtual bool GetCompositionCharacterBounds(uint32 index,
                                             gfx::Rect* rect) const OVERRIDE {
    // Emulate the situation of crbug.com/328237.
    if (emulate_pepper_flash_)
      return false;
    if (!rect || composition_character_bounds_.size() <= index)
      return false;
    *rect = composition_character_bounds_[index];
    return true;
  }
  virtual bool HasCompositionText() const OVERRIDE {
    return !composition_character_bounds_.empty();
  }
  virtual bool GetCompositionTextRange(gfx::Range* range) const OVERRIDE {
    if (composition_character_bounds_.empty())
      return false;
    *range = gfx::Range(0, composition_character_bounds_.size());
    return true;
  }
  virtual void OnCandidateWindowShown() OVERRIDE {
    is_candidate_window_shown_called_ = true;
  }
  virtual void OnCandidateWindowHidden() OVERRIDE {
    is_candidate_window_hidden_called_ = true;
  }

  ui::TextInputType text_input_type_;
  ui::TextInputMode text_input_mode_;
  gfx::Rect caret_bounds_;
  std::vector<gfx::Rect> composition_character_bounds_;
  base::string16 inserted_text_;
  size_t call_count_set_composition_text_;
  size_t call_count_insert_char_;
  size_t call_count_insert_text_;
  bool emulate_pepper_flash_;
  bool is_candidate_window_shown_called_;
  bool is_candidate_window_hidden_called_;
  DISALLOW_COPY_AND_ASSIGN(MockTextInputClient);
};

class MockInputMethodDelegate : public internal::InputMethodDelegate {
 public:
  MockInputMethodDelegate() {}

  const std::vector<ui::KeyboardCode>& fabricated_key_events() const {
    return fabricated_key_events_;
  }
  void Reset() {
    fabricated_key_events_.clear();
  }

 private:
  virtual bool DispatchKeyEventPostIME(const ui::KeyEvent& event) OVERRIDE {
    EXPECT_FALSE(event.HasNativeEvent());
    fabricated_key_events_.push_back(event.key_code());
    return true;
  }

  std::vector<ui::KeyboardCode> fabricated_key_events_;
  DISALLOW_COPY_AND_ASSIGN(MockInputMethodDelegate);
};

class MockRemoteInputMethodDelegateWin
    : public internal::RemoteInputMethodDelegateWin {
 public:
  MockRemoteInputMethodDelegateWin()
      : cancel_composition_called_(false),
        text_input_client_updated_called_(false) {
  }

  bool cancel_composition_called() const {
    return cancel_composition_called_;
  }
  bool text_input_client_updated_called() const {
    return text_input_client_updated_called_;
  }
  const std::vector<int32>& input_scopes() const {
    return input_scopes_;
  }
  const std::vector<gfx::Rect>& composition_character_bounds() const {
    return composition_character_bounds_;
  }
  void Reset() {
    cancel_composition_called_ = false;
    text_input_client_updated_called_ = false;
    input_scopes_.clear();
    composition_character_bounds_.clear();
  }

 private:
  virtual void CancelComposition() OVERRIDE {
    cancel_composition_called_ = true;
  }

  virtual void OnTextInputClientUpdated(
      const std::vector<int32>& input_scopes,
      const std::vector<gfx::Rect>& composition_character_bounds) OVERRIDE {
    text_input_client_updated_called_ = true;
    input_scopes_ = input_scopes;
    composition_character_bounds_ = composition_character_bounds;
  }

  bool cancel_composition_called_;
  bool text_input_client_updated_called_;
  std::vector<int32> input_scopes_;
  std::vector<gfx::Rect> composition_character_bounds_;
  DISALLOW_COPY_AND_ASSIGN(MockRemoteInputMethodDelegateWin);
};

class MockInputMethodObserver : public InputMethodObserver {
 public:
  MockInputMethodObserver()
      : on_text_input_state_changed_(0),
        on_input_method_destroyed_changed_(0) {
  }
  virtual ~MockInputMethodObserver() {
  }
  void Reset() {
    on_text_input_state_changed_ = 0;
    on_input_method_destroyed_changed_ = 0;
  }
  size_t on_text_input_state_changed() const {
    return on_text_input_state_changed_;
  }
  size_t on_input_method_destroyed_changed() const {
    return on_input_method_destroyed_changed_;
  }

 private:
  // Overriden from InputMethodObserver.
  virtual void OnTextInputTypeChanged(const TextInputClient* client) OVERRIDE {
  }
  virtual void OnFocus() OVERRIDE {
  }
  virtual void OnBlur() OVERRIDE {
  }
  virtual void OnCaretBoundsChanged(const TextInputClient* client) OVERRIDE {
  }
  virtual void OnTextInputStateChanged(const TextInputClient* client) OVERRIDE {
    ++on_text_input_state_changed_;
  }
  virtual void OnInputMethodDestroyed(const InputMethod* client) OVERRIDE {
    ++on_input_method_destroyed_changed_;
  }
  virtual void OnShowImeIfNeeded() {
  }

  size_t on_text_input_state_changed_;
  size_t on_input_method_destroyed_changed_;
  DISALLOW_COPY_AND_ASSIGN(MockInputMethodObserver);
};

typedef ScopedObserver<InputMethod, InputMethodObserver>
    InputMethodScopedObserver;

TEST(RemoteInputMethodWinTest, RemoteInputMethodPrivateWin) {
  InputMethod* other_ptr = static_cast<InputMethod*>(NULL) + 1;

  // Use typed NULL to make EXPECT_NE happy until nullptr becomes available.
  RemoteInputMethodPrivateWin* kNull =
      static_cast<RemoteInputMethodPrivateWin*>(NULL);
  EXPECT_EQ(kNull, RemoteInputMethodPrivateWin::Get(other_ptr));

  MockInputMethodDelegate delegate_;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));
  EXPECT_NE(kNull, RemoteInputMethodPrivateWin::Get(input_method.get()));

  InputMethod* dangling_ptr = input_method.get();
  input_method.reset(NULL);
  EXPECT_EQ(kNull, RemoteInputMethodPrivateWin::Get(dangling_ptr));
}

TEST(RemoteInputMethodWinTest, OnInputSourceChanged) {
  MockInputMethodDelegate delegate_;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));
  RemoteInputMethodPrivateWin* private_ptr =
      RemoteInputMethodPrivateWin::Get(input_method.get());
  ASSERT_TRUE(private_ptr != NULL);

  private_ptr->OnInputSourceChanged(
      MAKELANGID(LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN), true);
  EXPECT_EQ("ja-JP", input_method->GetInputLocale());

  private_ptr->OnInputSourceChanged(
      MAKELANGID(LANG_ARABIC, SUBLANG_ARABIC_QATAR), true);
  EXPECT_EQ("ar-QA", input_method->GetInputLocale());
}

TEST(RemoteInputMethodWinTest, OnCandidatePopupChanged) {
  MockInputMethodDelegate delegate_;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));
  RemoteInputMethodPrivateWin* private_ptr =
      RemoteInputMethodPrivateWin::Get(input_method.get());
  ASSERT_TRUE(private_ptr != NULL);

  // Initial value
  EXPECT_FALSE(input_method->IsCandidatePopupOpen());

  // RemoteInputMethodWin::OnCandidatePopupChanged can be called even when the
  // focused text input client is NULL.
  ASSERT_TRUE(input_method->GetTextInputClient() == NULL);
  private_ptr->OnCandidatePopupChanged(false);
  private_ptr->OnCandidatePopupChanged(true);

  MockTextInputClient mock_text_input_client;
  input_method->SetFocusedTextInputClient(&mock_text_input_client);

  ASSERT_FALSE(mock_text_input_client.is_candidate_window_shown_called());
  ASSERT_FALSE(mock_text_input_client.is_candidate_window_hidden_called());
  mock_text_input_client.Reset();

  private_ptr->OnCandidatePopupChanged(true);
  EXPECT_TRUE(input_method->IsCandidatePopupOpen());
  EXPECT_TRUE(mock_text_input_client.is_candidate_window_shown_called());
  EXPECT_FALSE(mock_text_input_client.is_candidate_window_hidden_called());

  private_ptr->OnCandidatePopupChanged(false);
  EXPECT_FALSE(input_method->IsCandidatePopupOpen());
  EXPECT_TRUE(mock_text_input_client.is_candidate_window_shown_called());
  EXPECT_TRUE(mock_text_input_client.is_candidate_window_hidden_called());
}

TEST(RemoteInputMethodWinTest, CancelComposition) {
  MockInputMethodDelegate delegate_;
  MockTextInputClient mock_text_input_client;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));

  // This must not cause a crash.
  input_method->CancelComposition(&mock_text_input_client);

  RemoteInputMethodPrivateWin* private_ptr =
      RemoteInputMethodPrivateWin::Get(input_method.get());
  ASSERT_TRUE(private_ptr != NULL);
  MockRemoteInputMethodDelegateWin mock_remote_delegate;
  private_ptr->SetRemoteDelegate(&mock_remote_delegate);

  input_method->CancelComposition(&mock_text_input_client);
  EXPECT_FALSE(mock_remote_delegate.cancel_composition_called());

  input_method->SetFocusedTextInputClient(&mock_text_input_client);
  input_method->CancelComposition(&mock_text_input_client);
  EXPECT_TRUE(mock_remote_delegate.cancel_composition_called());
}

TEST(RemoteInputMethodWinTest, SetFocusedTextInputClient) {
  MockInputMethodDelegate delegate_;
  MockTextInputClient mock_text_input_client;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));

  mock_text_input_client.set_caret_bounds(gfx::Rect(10, 0, 10, 20));
  mock_text_input_client.set_text_input_type(ui::TEXT_INPUT_TYPE_URL);
  input_method->SetFocusedTextInputClient(&mock_text_input_client);

  RemoteInputMethodPrivateWin* private_ptr =
      RemoteInputMethodPrivateWin::Get(input_method.get());
  ASSERT_TRUE(private_ptr != NULL);
  MockRemoteInputMethodDelegateWin mock_remote_delegate;
  private_ptr->SetRemoteDelegate(&mock_remote_delegate);

  // Initial state must be synced.
  EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called());
  ASSERT_EQ(1, mock_remote_delegate.composition_character_bounds().size());
  EXPECT_EQ(gfx::Rect(10, 0, 10, 20),
            mock_remote_delegate.composition_character_bounds()[0]);
  ASSERT_EQ(1, mock_remote_delegate.input_scopes().size());
  EXPECT_EQ(IS_URL, mock_remote_delegate.input_scopes()[0]);

  // State must be cleared by SetFocusedTextInputClient(NULL).
  mock_remote_delegate.Reset();
  input_method->SetFocusedTextInputClient(NULL);
  EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called());
  EXPECT_TRUE(mock_remote_delegate.composition_character_bounds().empty());
  EXPECT_TRUE(mock_remote_delegate.input_scopes().empty());
}

TEST(RemoteInputMethodWinTest, DetachTextInputClient) {
  MockInputMethodDelegate delegate_;
  MockTextInputClient mock_text_input_client;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));

  mock_text_input_client.set_caret_bounds(gfx::Rect(10, 0, 10, 20));
  mock_text_input_client.set_text_input_type(ui::TEXT_INPUT_TYPE_URL);
  input_method->SetFocusedTextInputClient(&mock_text_input_client);

  RemoteInputMethodPrivateWin* private_ptr =
      RemoteInputMethodPrivateWin::Get(input_method.get());
  ASSERT_TRUE(private_ptr != NULL);
  MockRemoteInputMethodDelegateWin mock_remote_delegate;
  private_ptr->SetRemoteDelegate(&mock_remote_delegate);

  // Initial state must be synced.
  EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called());
  ASSERT_EQ(1, mock_remote_delegate.composition_character_bounds().size());
  EXPECT_EQ(gfx::Rect(10, 0, 10, 20),
    mock_remote_delegate.composition_character_bounds()[0]);
  ASSERT_EQ(1, mock_remote_delegate.input_scopes().size());
  EXPECT_EQ(IS_URL, mock_remote_delegate.input_scopes()[0]);

  // State must be cleared by DetachTextInputClient
  mock_remote_delegate.Reset();
  input_method->DetachTextInputClient(&mock_text_input_client);
  EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called());
  EXPECT_TRUE(mock_remote_delegate.composition_character_bounds().empty());
  EXPECT_TRUE(mock_remote_delegate.input_scopes().empty());
}

TEST(RemoteInputMethodWinTest, OnCaretBoundsChanged) {
  MockInputMethodDelegate delegate_;
  MockTextInputClient mock_text_input_client;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));

  // This must not cause a crash.
  input_method->OnCaretBoundsChanged(&mock_text_input_client);

  mock_text_input_client.set_caret_bounds(gfx::Rect(10, 0, 10, 20));
  input_method->SetFocusedTextInputClient(&mock_text_input_client);

  RemoteInputMethodPrivateWin* private_ptr =
      RemoteInputMethodPrivateWin::Get(input_method.get());
  ASSERT_TRUE(private_ptr != NULL);
  MockRemoteInputMethodDelegateWin mock_remote_delegate;
  private_ptr->SetRemoteDelegate(&mock_remote_delegate);

  // Initial state must be synced.
  EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called());
  ASSERT_EQ(1, mock_remote_delegate.composition_character_bounds().size());
  EXPECT_EQ(gfx::Rect(10, 0, 10, 20),
      mock_remote_delegate.composition_character_bounds()[0]);

  // Redundant OnCaretBoundsChanged must be ignored.
  mock_remote_delegate.Reset();
  input_method->OnCaretBoundsChanged(&mock_text_input_client);
  EXPECT_FALSE(mock_remote_delegate.text_input_client_updated_called());

  // Check OnCaretBoundsChanged is handled. (w/o composition)
  mock_remote_delegate.Reset();
  mock_text_input_client.Reset();
  mock_text_input_client.set_caret_bounds(gfx::Rect(10, 20, 30, 40));
  input_method->OnCaretBoundsChanged(&mock_text_input_client);
  EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called());
  ASSERT_EQ(1, mock_remote_delegate.composition_character_bounds().size());
  EXPECT_EQ(gfx::Rect(10, 20, 30, 40),
      mock_remote_delegate.composition_character_bounds()[0]);

  // Check OnCaretBoundsChanged is handled. (w/ composition)
  {
    mock_remote_delegate.Reset();
    mock_text_input_client.Reset();

    std::vector<gfx::Rect> bounds;
    bounds.push_back(gfx::Rect(10, 20, 30, 40));
    bounds.push_back(gfx::Rect(40, 30, 20, 10));
    mock_text_input_client.set_composition_character_bounds(bounds);
    input_method->OnCaretBoundsChanged(&mock_text_input_client);
    EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called());
    EXPECT_EQ(bounds, mock_remote_delegate.composition_character_bounds());
  }
}

// Test case against crbug.com/328237.
TEST(RemoteInputMethodWinTest, OnCaretBoundsChangedForPepperFlash) {
  MockInputMethodDelegate delegate_;
  MockTextInputClient mock_text_input_client;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));
  input_method->SetFocusedTextInputClient(&mock_text_input_client);

  RemoteInputMethodPrivateWin* private_ptr =
      RemoteInputMethodPrivateWin::Get(input_method.get());
  ASSERT_TRUE(private_ptr != NULL);
  MockRemoteInputMethodDelegateWin mock_remote_delegate;
  private_ptr->SetRemoteDelegate(&mock_remote_delegate);

  mock_remote_delegate.Reset();
  mock_text_input_client.Reset();
  mock_text_input_client.set_emulate_pepper_flash(true);

  std::vector<gfx::Rect> caret_bounds;
  caret_bounds.push_back(gfx::Rect(5, 15, 25, 35));
  mock_text_input_client.set_caret_bounds(caret_bounds[0]);

  std::vector<gfx::Rect> composition_bounds;
  composition_bounds.push_back(gfx::Rect(10, 20, 30, 40));
  composition_bounds.push_back(gfx::Rect(40, 30, 20, 10));
  mock_text_input_client.set_composition_character_bounds(composition_bounds);
  input_method->OnCaretBoundsChanged(&mock_text_input_client);
  EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called());
  // The caret bounds must be used when
  // TextInputClient::GetCompositionCharacterBounds failed.
  EXPECT_EQ(caret_bounds, mock_remote_delegate.composition_character_bounds());
}

TEST(RemoteInputMethodWinTest, OnTextInputTypeChanged) {
  MockInputMethodDelegate delegate_;
  MockTextInputClient mock_text_input_client;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));

  // This must not cause a crash.
  input_method->OnCaretBoundsChanged(&mock_text_input_client);

  mock_text_input_client.set_text_input_type(ui::TEXT_INPUT_TYPE_URL);
  input_method->SetFocusedTextInputClient(&mock_text_input_client);

  RemoteInputMethodPrivateWin* private_ptr =
      RemoteInputMethodPrivateWin::Get(input_method.get());
  ASSERT_TRUE(private_ptr != NULL);
  MockRemoteInputMethodDelegateWin mock_remote_delegate;
  private_ptr->SetRemoteDelegate(&mock_remote_delegate);

  // Initial state must be synced.
  EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called());
  ASSERT_EQ(1, mock_remote_delegate.input_scopes().size());
  EXPECT_EQ(IS_URL, mock_remote_delegate.input_scopes()[0]);

  // Check TEXT_INPUT_TYPE_NONE is handled.
  mock_remote_delegate.Reset();
  mock_text_input_client.Reset();
  mock_text_input_client.set_text_input_type(ui::TEXT_INPUT_TYPE_NONE);
  mock_text_input_client.set_text_input_mode(ui::TEXT_INPUT_MODE_KATAKANA);
  input_method->OnTextInputTypeChanged(&mock_text_input_client);
  EXPECT_TRUE(mock_remote_delegate.text_input_client_updated_called());
  EXPECT_TRUE(mock_remote_delegate.input_scopes().empty());

  // Redundant OnTextInputTypeChanged must be ignored.
  mock_remote_delegate.Reset();
  input_method->OnTextInputTypeChanged(&mock_text_input_client);
  EXPECT_FALSE(mock_remote_delegate.text_input_client_updated_called());

  mock_remote_delegate.Reset();
  mock_text_input_client.Reset();
  mock_text_input_client.set_caret_bounds(gfx::Rect(10, 20, 30, 40));
  input_method->OnCaretBoundsChanged(&mock_text_input_client);
}

TEST(RemoteInputMethodWinTest, DispatchKeyEvent_NativeKeyEvent) {
  // Basically RemoteInputMethodWin does not handle native keydown event.

  MockInputMethodDelegate delegate_;
  MockTextInputClient mock_text_input_client;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));

  const MSG wm_keydown = { NULL, WM_KEYDOWN, ui::VKEY_A };
  ui::KeyEvent native_keydown(wm_keydown, false);

  // This must not cause a crash.
  EXPECT_FALSE(input_method->DispatchKeyEvent(native_keydown));
  EXPECT_TRUE(mock_text_input_client.inserted_text().empty());
  EXPECT_TRUE(delegate_.fabricated_key_events().empty());
  delegate_.Reset();
  mock_text_input_client.Reset();

  RemoteInputMethodPrivateWin* private_ptr =
      RemoteInputMethodPrivateWin::Get(input_method.get());
  ASSERT_TRUE(private_ptr != NULL);
  MockRemoteInputMethodDelegateWin mock_remote_delegate;
  private_ptr->SetRemoteDelegate(&mock_remote_delegate);

  // TextInputClient is not focused yet here.

  EXPECT_FALSE(input_method->DispatchKeyEvent(native_keydown));
  EXPECT_TRUE(mock_text_input_client.inserted_text().empty());
  EXPECT_TRUE(delegate_.fabricated_key_events().empty());
  delegate_.Reset();
  mock_text_input_client.Reset();

  input_method->SetFocusedTextInputClient(&mock_text_input_client);

  // TextInputClient is now focused here.

  EXPECT_FALSE(input_method->DispatchKeyEvent(native_keydown));
  EXPECT_TRUE(mock_text_input_client.inserted_text().empty());
  EXPECT_TRUE(delegate_.fabricated_key_events().empty());
  delegate_.Reset();
  mock_text_input_client.Reset();
}

TEST(RemoteInputMethodWinTest, DispatchKeyEvent_NativeCharEvent) {
  // RemoteInputMethodWin handles native char event if possible.

  MockInputMethodDelegate delegate_;
  MockTextInputClient mock_text_input_client;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));

  const MSG wm_char = { NULL, WM_CHAR, 'A', 0 };
  ui::KeyEvent native_char(wm_char, true);

  // This must not cause a crash.
  EXPECT_FALSE(input_method->DispatchKeyEvent(native_char));
  EXPECT_TRUE(mock_text_input_client.inserted_text().empty());
  EXPECT_TRUE(delegate_.fabricated_key_events().empty());
  delegate_.Reset();
  mock_text_input_client.Reset();

  RemoteInputMethodPrivateWin* private_ptr =
      RemoteInputMethodPrivateWin::Get(input_method.get());
  ASSERT_TRUE(private_ptr != NULL);
  MockRemoteInputMethodDelegateWin mock_remote_delegate;
  private_ptr->SetRemoteDelegate(&mock_remote_delegate);

  // TextInputClient is not focused yet here.

  EXPECT_FALSE(input_method->DispatchKeyEvent(native_char));
  EXPECT_TRUE(mock_text_input_client.inserted_text().empty());
  EXPECT_TRUE(delegate_.fabricated_key_events().empty());
  delegate_.Reset();
  mock_text_input_client.Reset();

  input_method->SetFocusedTextInputClient(&mock_text_input_client);

  // TextInputClient is now focused here.

  EXPECT_TRUE(input_method->DispatchKeyEvent(native_char));
  EXPECT_EQ(L"A", mock_text_input_client.inserted_text());
  EXPECT_TRUE(delegate_.fabricated_key_events().empty());
  delegate_.Reset();
  mock_text_input_client.Reset();
}

TEST(RemoteInputMethodWinTest, DispatchKeyEvent_FabricatedKeyDown) {
  // Fabricated non-char event will be delegated to
  // InputMethodDelegate::DispatchFabricatedKeyEventPostIME as long as the
  // delegate is installed.

  MockInputMethodDelegate delegate_;
  MockTextInputClient mock_text_input_client;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));

  ui::KeyEvent fabricated_keydown(ui::ET_KEY_PRESSED, ui::VKEY_A, 0, false);
  fabricated_keydown.set_character(L'A');

  // This must not cause a crash.
  EXPECT_TRUE(input_method->DispatchKeyEvent(fabricated_keydown));
  EXPECT_TRUE(mock_text_input_client.inserted_text().empty());
  ASSERT_EQ(1, delegate_.fabricated_key_events().size());
  EXPECT_EQ(L'A', delegate_.fabricated_key_events()[0]);
  delegate_.Reset();
  mock_text_input_client.Reset();

  RemoteInputMethodPrivateWin* private_ptr =
      RemoteInputMethodPrivateWin::Get(input_method.get());
  ASSERT_TRUE(private_ptr != NULL);
  MockRemoteInputMethodDelegateWin mock_remote_delegate;
  private_ptr->SetRemoteDelegate(&mock_remote_delegate);

  // TextInputClient is not focused yet here.

  EXPECT_TRUE(input_method->DispatchKeyEvent(fabricated_keydown));
  EXPECT_TRUE(mock_text_input_client.inserted_text().empty());
  ASSERT_EQ(1, delegate_.fabricated_key_events().size());
  EXPECT_EQ(L'A', delegate_.fabricated_key_events()[0]);
  delegate_.Reset();
  mock_text_input_client.Reset();

  input_method->SetFocusedTextInputClient(&mock_text_input_client);
  // TextInputClient is now focused here.

  EXPECT_TRUE(input_method->DispatchKeyEvent(fabricated_keydown));
  EXPECT_TRUE(mock_text_input_client.inserted_text().empty());
  ASSERT_EQ(1, delegate_.fabricated_key_events().size());
  EXPECT_EQ(L'A', delegate_.fabricated_key_events()[0]);
  delegate_.Reset();
  mock_text_input_client.Reset();

  input_method->SetDelegate(NULL);
  // RemoteInputMethodDelegateWin is no longer set here.

  EXPECT_FALSE(input_method->DispatchKeyEvent(fabricated_keydown));
  EXPECT_TRUE(mock_text_input_client.inserted_text().empty());
}

TEST(RemoteInputMethodWinTest, DispatchKeyEvent_FabricatedChar) {
  // Note: RemoteInputMethodWin::DispatchKeyEvent should always return true
  // for fabricated character events.

  MockInputMethodDelegate delegate_;
  MockTextInputClient mock_text_input_client;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));

  ui::KeyEvent fabricated_char(ui::ET_KEY_PRESSED, ui::VKEY_A, 0, true);
  fabricated_char.set_character(L'A');

  // This must not cause a crash.
  EXPECT_TRUE(input_method->DispatchKeyEvent(fabricated_char));
  EXPECT_TRUE(mock_text_input_client.inserted_text().empty());
  EXPECT_TRUE(delegate_.fabricated_key_events().empty());
  delegate_.Reset();
  mock_text_input_client.Reset();

  RemoteInputMethodPrivateWin* private_ptr =
      RemoteInputMethodPrivateWin::Get(input_method.get());
  ASSERT_TRUE(private_ptr != NULL);
  MockRemoteInputMethodDelegateWin mock_remote_delegate;
  private_ptr->SetRemoteDelegate(&mock_remote_delegate);

  // TextInputClient is not focused yet here.

  EXPECT_TRUE(input_method->DispatchKeyEvent(fabricated_char));
  EXPECT_TRUE(mock_text_input_client.inserted_text().empty());
  EXPECT_TRUE(delegate_.fabricated_key_events().empty());
  delegate_.Reset();
  mock_text_input_client.Reset();

  input_method->SetFocusedTextInputClient(&mock_text_input_client);

  // TextInputClient is now focused here.

  EXPECT_TRUE(input_method->DispatchKeyEvent(fabricated_char));
  EXPECT_EQ(L"A", mock_text_input_client.inserted_text());
  EXPECT_TRUE(delegate_.fabricated_key_events().empty());
  delegate_.Reset();
  mock_text_input_client.Reset();
}

TEST(RemoteInputMethodWinTest, OnCompositionChanged) {
  MockInputMethodDelegate delegate_;
  MockTextInputClient mock_text_input_client;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));

  RemoteInputMethodPrivateWin* private_ptr =
      RemoteInputMethodPrivateWin::Get(input_method.get());
  ASSERT_TRUE(private_ptr != NULL);
  MockRemoteInputMethodDelegateWin mock_remote_delegate;
  private_ptr->SetRemoteDelegate(&mock_remote_delegate);

  CompositionText composition_text;

  // TextInputClient is not focused yet here.

  private_ptr->OnCompositionChanged(composition_text);
  EXPECT_EQ(0, mock_text_input_client.call_count_set_composition_text());
  delegate_.Reset();
  mock_text_input_client.Reset();

  input_method->SetFocusedTextInputClient(&mock_text_input_client);

  // TextInputClient is now focused here.

  private_ptr->OnCompositionChanged(composition_text);
  EXPECT_EQ(1, mock_text_input_client.call_count_set_composition_text());
  delegate_.Reset();
  mock_text_input_client.Reset();
}

TEST(RemoteInputMethodWinTest, OnTextCommitted) {
  MockInputMethodDelegate delegate_;
  MockTextInputClient mock_text_input_client;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));

  RemoteInputMethodPrivateWin* private_ptr =
      RemoteInputMethodPrivateWin::Get(input_method.get());
  ASSERT_TRUE(private_ptr != NULL);
  MockRemoteInputMethodDelegateWin mock_remote_delegate;
  private_ptr->SetRemoteDelegate(&mock_remote_delegate);

  base::string16 committed_text = L"Hello";

  // TextInputClient is not focused yet here.

  mock_text_input_client.set_text_input_type(TEXT_INPUT_TYPE_TEXT);
  private_ptr->OnTextCommitted(committed_text);
  EXPECT_EQ(0, mock_text_input_client.call_count_insert_char());
  EXPECT_EQ(0, mock_text_input_client.call_count_insert_text());
  EXPECT_EQ(L"", mock_text_input_client.inserted_text());
  delegate_.Reset();
  mock_text_input_client.Reset();

  input_method->SetFocusedTextInputClient(&mock_text_input_client);

  // TextInputClient is now focused here.

  mock_text_input_client.set_text_input_type(TEXT_INPUT_TYPE_TEXT);
  private_ptr->OnTextCommitted(committed_text);
  EXPECT_EQ(0, mock_text_input_client.call_count_insert_char());
  EXPECT_EQ(1, mock_text_input_client.call_count_insert_text());
  EXPECT_EQ(committed_text, mock_text_input_client.inserted_text());
  delegate_.Reset();
  mock_text_input_client.Reset();

  // When TextInputType is TEXT_INPUT_TYPE_NONE, TextInputClient::InsertText
  // should not be used.
  mock_text_input_client.set_text_input_type(TEXT_INPUT_TYPE_NONE);
  private_ptr->OnTextCommitted(committed_text);
  EXPECT_EQ(committed_text.size(),
            mock_text_input_client.call_count_insert_char());
  EXPECT_EQ(0, mock_text_input_client.call_count_insert_text());
  EXPECT_EQ(committed_text, mock_text_input_client.inserted_text());
  delegate_.Reset();
  mock_text_input_client.Reset();
}

TEST(RemoteInputMethodWinTest, OnTextInputStateChanged_Observer) {
  DummyTextInputClient text_input_client;
  DummyTextInputClient text_input_client_the_other;

  MockInputMethodObserver input_method_observer;
  MockInputMethodDelegate delegate_;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));
  InputMethodScopedObserver scoped_observer(&input_method_observer);
  scoped_observer.Add(input_method.get());

  input_method->SetFocusedTextInputClient(&text_input_client);
  ASSERT_EQ(&text_input_client, input_method->GetTextInputClient());
  EXPECT_EQ(1u, input_method_observer.on_text_input_state_changed());
  input_method_observer.Reset();

  input_method->SetFocusedTextInputClient(&text_input_client);
  ASSERT_EQ(&text_input_client, input_method->GetTextInputClient());
  EXPECT_EQ(0u, input_method_observer.on_text_input_state_changed());
  input_method_observer.Reset();

  input_method->SetFocusedTextInputClient(&text_input_client_the_other);
  ASSERT_EQ(&text_input_client_the_other, input_method->GetTextInputClient());
  EXPECT_EQ(1u, input_method_observer.on_text_input_state_changed());
  input_method_observer.Reset();

  input_method->DetachTextInputClient(&text_input_client_the_other);
  ASSERT_TRUE(input_method->GetTextInputClient() == NULL);
  EXPECT_EQ(1u, input_method_observer.on_text_input_state_changed());
  input_method_observer.Reset();
}

TEST(RemoteInputMethodWinTest, OnInputMethodDestroyed_Observer) {
  DummyTextInputClient text_input_client;
  DummyTextInputClient text_input_client_the_other;

  MockInputMethodObserver input_method_observer;
  InputMethodScopedObserver scoped_observer(&input_method_observer);

  MockInputMethodDelegate delegate_;
  scoped_ptr<InputMethod> input_method(CreateRemoteInputMethodWin(&delegate_));
  input_method->AddObserver(&input_method_observer);

  EXPECT_EQ(0u, input_method_observer.on_input_method_destroyed_changed());
  input_method.reset();
  EXPECT_EQ(1u, input_method_observer.on_input_method_destroyed_changed());
}

}  // namespace
}  // namespace ui

/* [<][>][^][v][top][bottom][index][help] */