// Copyright (c) 2011 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 <vector>

#include "base/auto_reset.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/range/range.h"
#include "ui/gfx/render_text.h"
#include "views/controls/textfield/textfield.h"
#include "views/controls/textfield/textfield_views_model.h"
#include "views/test/test_views_delegate.h"
#include "views/test/views_test_base.h"
#include "views/views_delegate.h"

namespace {

struct WordAndCursor {
  WordAndCursor(const wchar_t* w, size_t c)
      : word(w),
        cursor(c) {
  }

  const wchar_t* word;
  size_t cursor;
};

}  // namespace

namespace views {

class TextfieldViewsModelTest : public ViewsTestBase,
                                public TextfieldViewsModel::Delegate {
 public:
  TextfieldViewsModelTest()
      : ViewsTestBase(),
        composition_text_confirmed_or_cleared_(false) {
  }

  virtual void OnCompositionTextConfirmedOrCleared() {
    composition_text_confirmed_or_cleared_ = true;
  }

 protected:
  void ResetModel(TextfieldViewsModel* model) const {
    model->SetText(ASCIIToUTF16(""));
    model->ClearEditHistory();
  }

  bool composition_text_confirmed_or_cleared_;

 private:
  DISALLOW_COPY_AND_ASSIGN(TextfieldViewsModelTest);
};

#define EXPECT_STR_EQ(ascii, utf16) \
  EXPECT_EQ(ASCIIToWide(ascii), UTF16ToWide(utf16))
TEST_F(TextfieldViewsModelTest, EditString) {
  TextfieldViewsModel model(NULL);
  // append two strings
  model.Append(ASCIIToUTF16("HILL"));
  EXPECT_STR_EQ("HILL", model.GetText());
  model.Append(ASCIIToUTF16("WORLD"));
  EXPECT_STR_EQ("HILLWORLD", model.GetText());

  // Insert "E" to make hello
  model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
  model.InsertChar('E');
  EXPECT_STR_EQ("HEILLWORLD", model.GetText());
  // Replace "I" with "L"
  model.ReplaceChar('L');
  EXPECT_STR_EQ("HELLLWORLD", model.GetText());
  model.ReplaceChar('L');
  model.ReplaceChar('O');
  EXPECT_STR_EQ("HELLOWORLD", model.GetText());

  // Delete 6th char "W", then delete 5th char O"
  EXPECT_EQ(5U, model.GetCursorPosition());
  EXPECT_TRUE(model.Delete());
  EXPECT_STR_EQ("HELLOORLD", model.GetText());
  EXPECT_TRUE(model.Backspace());
  EXPECT_EQ(4U, model.GetCursorPosition());
  EXPECT_STR_EQ("HELLORLD", model.GetText());

  // Move the cursor to start. backspace should fail.
  model.MoveCursorLeft(gfx::LINE_BREAK, false);
  EXPECT_FALSE(model.Backspace());
  EXPECT_STR_EQ("HELLORLD", model.GetText());
  // Move the cursor to the end. delete should fail.
  model.MoveCursorRight(gfx::LINE_BREAK, false);
  EXPECT_FALSE(model.Delete());
  EXPECT_STR_EQ("HELLORLD", model.GetText());
  // but backspace should work.
  EXPECT_TRUE(model.Backspace());
  EXPECT_STR_EQ("HELLORL", model.GetText());

  model.MoveCursorTo(gfx::SelectionModel(5));
  model.ReplaceText(ASCIIToUTF16(" WOR"));
  EXPECT_STR_EQ("HELLO WORL", model.GetText());
}

TEST_F(TextfieldViewsModelTest, EditString_SimpleRTL) {
  TextfieldViewsModel model(NULL);
  // Append two strings.
  model.Append(WideToUTF16(L"\x05d0\x05d1\x05d2"));
  EXPECT_EQ(WideToUTF16(L"\x05d0\x05d1\x05d2"), model.GetText());
  model.Append(WideToUTF16(L"\x05e0\x05e1\x05e2"));
  EXPECT_EQ(WideToUTF16(L"\x05d0\x05d1\x05d2\x05e0\x05e1\x05e2"),
            model.GetText());

  // Insert 0x05f0.
  model.MoveCursorTo(gfx::SelectionModel(1U));
  model.InsertChar(0x05f0);
  EXPECT_EQ(WideToUTF16(L"\x05d0\x05f0\x05d1\x05d2\x05e0\x05e1\x05e2"),
            model.GetText());

  // Replace "\x05d1" with "\x05f1".
  model.ReplaceChar(0x05f1);
  EXPECT_EQ(WideToUTF16(L"\x05d0\x05f0\x5f1\x05d2\x05e0\x05e1\x05e2"),
            model.GetText());

  // Delete and backspace.
  EXPECT_EQ(3U, model.GetCursorPosition());
  EXPECT_TRUE(model.Delete());
  EXPECT_EQ(WideToUTF16(L"\x05d0\x05f0\x5f1\x05e0\x05e1\x05e2"),
            model.GetText());
  EXPECT_TRUE(model.Backspace());
  EXPECT_EQ(2U, model.GetCursorPosition());
  EXPECT_EQ(WideToUTF16(L"\x05d0\x05f0\x05e0\x05e1\x05e2"), model.GetText());
}

TEST_F(TextfieldViewsModelTest, EditString_ComplexScript) {
  TextfieldViewsModel model(NULL);
  // Append two Hindi strings.
  model.Append(WideToUTF16(L"\x0915\x093f\x0915\x094d\x0915"));
  EXPECT_EQ(WideToUTF16(L"\x0915\x093f\x0915\x094d\x0915"),
            model.GetText());
  model.Append(WideToUTF16(L"\x0915\x094d\x092e\x094d"));
  EXPECT_EQ(WideToUTF16(
      L"\x0915\x093f\x0915\x094d\x0915\x0915\x094d\x092e\x094d"),
      model.GetText());

  // Check it is not able to place cursor in middle of a grapheme.
  // TODO(xji): temporarily disable in platform Win since the complex script
  // characters turned into empty square due to font regression. So, not able
  // to test 2 characters belong to the same grapheme.
#if defined(OS_LINUX)
  model.MoveCursorTo(gfx::SelectionModel(1U));
  EXPECT_EQ(0U, model.GetCursorPosition());
#endif

  model.MoveCursorTo(gfx::SelectionModel(2U));
  EXPECT_EQ(2U, model.GetCursorPosition());
  model.InsertChar('a');
  EXPECT_EQ(WideToUTF16(
      L"\x0915\x093f\x0061\x0915\x094d\x0915\x0915\x094d\x092e\x094d"),
      model.GetText());

  // ReplaceChar will replace the whole grapheme.
  model.ReplaceChar('b');
  // TODO(xji): temporarily disable in platform Win since the complex script
  // characters turned into empty square due to font regression. So, not able
  // to test 2 characters belong to the same grapheme.
#if defined(OS_LINUX)
  EXPECT_EQ(WideToUTF16(
      L"\x0915\x093f\x0061\x0062\x0915\x0915\x094d\x092e\x094d"),
      model.GetText());
#endif
  EXPECT_EQ(4U, model.GetCursorPosition());

  // Delete should delete the whole grapheme.
  model.MoveCursorTo(gfx::SelectionModel(0U));
  // TODO(xji): temporarily disable in platform Win since the complex script
  // characters turned into empty square due to font regression. So, not able
  // to test 2 characters belong to the same grapheme.
#if defined(OS_LINUX)
  EXPECT_TRUE(model.Delete());
  EXPECT_EQ(WideToUTF16(L"\x0061\x0062\x0915\x0915\x094d\x092e\x094d"),
            model.GetText());
  model.MoveCursorTo(gfx::SelectionModel(model.GetText().length()));
  EXPECT_TRUE(model.Backspace());
  EXPECT_EQ(WideToUTF16(L"\x0061\x0062\x0915\x0915\x094d\x092e"),
            model.GetText());
#endif

  // Test cursor position and deletion for Hindi Virama.
  model.SetText(WideToUTF16(L"\x0D38\x0D4D\x0D15\x0D16\x0D2E"));
  model.MoveCursorTo(gfx::SelectionModel(0));
  EXPECT_EQ(0U, model.GetCursorPosition());

  // TODO(xji): temporarily disable in platform Win since the complex script
  // characters turned into empty square due to font regression. So, not able
  // to test 2 characters belong to the same grapheme.
#if defined(OS_LINUX)
  model.MoveCursorTo(gfx::SelectionModel(1));
  EXPECT_EQ(0U, model.GetCursorPosition());
#endif

  model.MoveCursorTo(gfx::SelectionModel(2));
  EXPECT_EQ(2U, model.GetCursorPosition());

  model.MoveCursorTo(gfx::SelectionModel(3));
  EXPECT_EQ(3U, model.GetCursorPosition());

  model.MoveCursorTo(gfx::SelectionModel(2));

  EXPECT_TRUE(model.Backspace());
  EXPECT_EQ(WideToUTF16(L"\x0D38\x0D15\x0D16\x0D2E"), model.GetText());

  // Test Delete/Backspace on Hebrew with non-spacing marks.
  // TODO(xji): temporarily disable in platform Win since the complex script
  // characters turned into empty square due to font regression. So, not able
  // to test 2 characters belong to the same grapheme.
#if defined(OS_LINUX)
  model.SetText(WideToUTF16(L"\x05d5\x05b7\x05D9\x05B0\x05D4\x05B4\x05D9"));
  model.MoveCursorTo(gfx::SelectionModel(0));
  EXPECT_TRUE(model.Delete());
  EXPECT_TRUE(model.Delete());
  EXPECT_TRUE(model.Delete());
  EXPECT_TRUE(model.Delete());
  EXPECT_EQ(WideToUTF16(L""), model.GetText());
#endif

  // The first 2 characters are not strong directionality characters.
  model.SetText(WideToUTF16(L"\x002C\x0020\x05D1\x05BC\x05B7\x05E9\x05BC"));
#if defined(OS_WIN)
  model.MoveCursorRight(gfx::LINE_BREAK, false);
#else
  model.MoveCursorLeft(gfx::LINE_BREAK, false);
#endif
  EXPECT_TRUE(model.Backspace());
  EXPECT_EQ(WideToUTF16(L"\x002C\x0020\x05D1\x05BC\x05B7\x05E9"),
            model.GetText());
}

TEST_F(TextfieldViewsModelTest, EmptyString) {
  TextfieldViewsModel model(NULL);
  EXPECT_EQ(string16(), model.GetText());
  EXPECT_EQ(string16(), model.GetSelectedText());
  EXPECT_EQ(string16(), model.GetVisibleText());

  model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
  EXPECT_EQ(0U, model.GetCursorPosition());
  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  EXPECT_EQ(0U, model.GetCursorPosition());

  EXPECT_EQ(string16(), model.GetSelectedText());

  EXPECT_FALSE(model.Delete());
  EXPECT_FALSE(model.Backspace());
}

TEST_F(TextfieldViewsModelTest, Selection) {
  TextfieldViewsModel model(NULL);
  model.Append(ASCIIToUTF16("HELLO"));
  model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  EXPECT_STR_EQ("E", model.GetSelectedText());
  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  EXPECT_STR_EQ("EL", model.GetSelectedText());

  model.MoveCursorLeft(gfx::LINE_BREAK, true);
  EXPECT_STR_EQ("H", model.GetSelectedText());
  model.MoveCursorRight(gfx::LINE_BREAK, true);
  EXPECT_STR_EQ("ELLO", model.GetSelectedText());
  model.ClearSelection();
  EXPECT_EQ(string16(), model.GetSelectedText());
  model.SelectAll();
  EXPECT_STR_EQ("HELLO", model.GetSelectedText());
  // SelectAll should select towards the end.
  gfx::SelectionModel sel;
  model.GetSelectionModel(&sel);
  EXPECT_EQ(0U, sel.selection_start());
  EXPECT_EQ(5U, sel.selection_end());

  // Select and move cursor
  model.MoveCursorTo(gfx::SelectionModel(1U, 3U));
  EXPECT_STR_EQ("EL", model.GetSelectedText());
  model.MoveCursorLeft(gfx::CHARACTER_BREAK, false);
  EXPECT_EQ(1U, model.GetCursorPosition());
  model.MoveCursorTo(gfx::SelectionModel(1U, 3U));
  model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
  EXPECT_EQ(3U, model.GetCursorPosition());

  // Select all and move cursor
  model.SelectAll();
  model.MoveCursorLeft(gfx::CHARACTER_BREAK, false);
  EXPECT_EQ(0U, model.GetCursorPosition());
  model.SelectAll();
  model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
  EXPECT_EQ(5U, model.GetCursorPosition());
}

TEST_F(TextfieldViewsModelTest, Selection_BidiWithNonSpacingMarks) {
  // Selection is a logical operation. And it should work with the arrow
  // keys doing visual movements, while the selection is logical between
  // the (logical) start and end points. Selection is simply defined as
  // the portion of text between the logical positions of the start and end
  // caret positions.
  TextfieldViewsModel model(NULL);
  // TODO(xji): temporarily disable in platform Win since the complex script
  // characters turned into empty square due to font regression. So, not able
  // to test 2 characters belong to the same grapheme.
#if defined(OS_LINUX)
  model.Append(WideToUTF16(
      L"abc\x05E9\x05BC\x05C1\x05B8\x05E0\x05B8"L"def"));
  model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
  model.MoveCursorRight(gfx::CHARACTER_BREAK, false);

  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  EXPECT_EQ(2U, model.render_text()->GetSelectionStart());
  EXPECT_EQ(3U, model.GetCursorPosition());
  EXPECT_EQ(WideToUTF16(L"c"), model.GetSelectedText());

  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  EXPECT_EQ(2U, model.render_text()->GetSelectionStart());
  EXPECT_EQ(7U, model.GetCursorPosition());
  EXPECT_EQ(WideToUTF16(L"c\x05E9\x05BC\x05C1\x05B8"),
            model.GetSelectedText());

  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  EXPECT_EQ(2U, model.render_text()->GetSelectionStart());
  EXPECT_EQ(3U, model.GetCursorPosition());
  EXPECT_EQ(WideToUTF16(L"c"), model.GetSelectedText());

  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  EXPECT_EQ(2U, model.render_text()->GetSelectionStart());
  EXPECT_EQ(10U, model.GetCursorPosition());
  EXPECT_EQ(WideToUTF16(L"c\x05E9\x05BC\x05C1\x05B8\x05E0\x05B8"L"d"),
            model.GetSelectedText());

  model.ClearSelection();
  EXPECT_EQ(string16(), model.GetSelectedText());
  model.SelectAll();
  EXPECT_EQ(WideToUTF16(L"abc\x05E9\x05BC\x05C1\x05B8\x05E0\x05B8"L"def"),
            model.GetSelectedText());
#endif

  // In case of "aBc", this test shows how to select "aB" or "Bc", assume 'B' is
  // an RTL character.
  model.SetText(WideToUTF16(L"a\x05E9"L"b"));
  model.MoveCursorTo(gfx::SelectionModel(0));
  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  EXPECT_EQ(WideToUTF16(L"a"), model.GetSelectedText());

  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  EXPECT_EQ(WideToUTF16(L"a"), model.GetSelectedText());

  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  EXPECT_EQ(WideToUTF16(L"a\x05E9"L"b"), model.GetSelectedText());

  model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
  EXPECT_EQ(3U, model.GetCursorPosition());
  model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
  EXPECT_EQ(WideToUTF16(L"b"), model.GetSelectedText());

  model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
  EXPECT_EQ(WideToUTF16(L"b"), model.GetSelectedText());

  model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
  EXPECT_EQ(WideToUTF16(L"a\x05E9"L"b"), model.GetSelectedText());

  model.MoveCursorLeft(gfx::LINE_BREAK, false);
  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
  EXPECT_EQ(WideToUTF16(L"a\x05E9"), model.GetSelectedText());

  model.MoveCursorRight(gfx::LINE_BREAK, false);
  model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
  model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  EXPECT_EQ(WideToUTF16(L"\x05E9"L"b"), model.GetSelectedText());

  model.ClearSelection();
  EXPECT_EQ(string16(), model.GetSelectedText());
  model.SelectAll();
  EXPECT_EQ(WideToUTF16(L"a\x05E9"L"b"), model.GetSelectedText());
}

TEST_F(TextfieldViewsModelTest, SelectionAndEdit) {
  TextfieldViewsModel model(NULL);
  model.Append(ASCIIToUTF16("HELLO"));
  model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);  // select "EL"
  EXPECT_TRUE(model.Backspace());
  EXPECT_STR_EQ("HLO", model.GetText());

  model.Append(ASCIIToUTF16("ILL"));
  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);  // select "LO"
  EXPECT_TRUE(model.Delete());
  EXPECT_STR_EQ("HILL", model.GetText());
  EXPECT_EQ(1U, model.GetCursorPosition());
  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);  // select "I"
  model.InsertChar('E');
  EXPECT_STR_EQ("HELL", model.GetText());
  model.MoveCursorLeft(gfx::LINE_BREAK, false);
  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);  // select "H"
  model.ReplaceChar('B');
  EXPECT_STR_EQ("BELL", model.GetText());
  model.MoveCursorRight(gfx::LINE_BREAK, false);
  model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
  model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);  // select "ELL"
  model.ReplaceChar('E');
  EXPECT_STR_EQ("BEE", model.GetText());
}

TEST_F(TextfieldViewsModelTest, Password) {
  TextfieldViewsModel model(NULL);
  model.set_is_password(true);
  model.Append(ASCIIToUTF16("HELLO"));
  EXPECT_STR_EQ("*****", model.GetVisibleText());
  EXPECT_STR_EQ("HELLO", model.GetText());
  EXPECT_TRUE(model.Delete());

  EXPECT_STR_EQ("****", model.GetVisibleText());
  EXPECT_STR_EQ("ELLO", model.GetText());
  EXPECT_EQ(0U, model.GetCursorPosition());

  model.SelectAll();
  EXPECT_STR_EQ("ELLO", model.GetSelectedText());
  EXPECT_EQ(4U, model.GetCursorPosition());

  model.InsertChar('X');
  EXPECT_STR_EQ("*", model.GetVisibleText());
  EXPECT_STR_EQ("X", model.GetText());
}

TEST_F(TextfieldViewsModelTest, Word) {
  TextfieldViewsModel model(NULL);
  model.Append(
      ASCIIToUTF16("The answer to Life, the Universe, and Everything"));
  model.MoveCursorRight(gfx::WORD_BREAK, false);
  EXPECT_EQ(3U, model.GetCursorPosition());
  model.MoveCursorRight(gfx::WORD_BREAK, false);
  EXPECT_EQ(10U, model.GetCursorPosition());
  model.MoveCursorRight(gfx::WORD_BREAK, false);
  model.MoveCursorRight(gfx::WORD_BREAK, false);
  EXPECT_EQ(18U, model.GetCursorPosition());

  // Should passes the non word char ','
  model.MoveCursorRight(gfx::WORD_BREAK, true);
  EXPECT_EQ(23U, model.GetCursorPosition());
  EXPECT_STR_EQ(", the", model.GetSelectedText());

  // Move to the end.
  model.MoveCursorRight(gfx::WORD_BREAK, true);
  model.MoveCursorRight(gfx::WORD_BREAK, true);
  model.MoveCursorRight(gfx::WORD_BREAK, true);
  EXPECT_STR_EQ(", the Universe, and Everything", model.GetSelectedText());
  // Should be safe to go next word at the end.
  model.MoveCursorRight(gfx::WORD_BREAK, true);
  EXPECT_STR_EQ(", the Universe, and Everything", model.GetSelectedText());
  model.InsertChar('2');
  EXPECT_EQ(19U, model.GetCursorPosition());

  // Now backwards.
  model.MoveCursorLeft(gfx::CHARACTER_BREAK, false);  // leave 2.
  model.MoveCursorLeft(gfx::WORD_BREAK, true);
  EXPECT_EQ(14U, model.GetCursorPosition());
  EXPECT_STR_EQ("Life", model.GetSelectedText());
  model.MoveCursorLeft(gfx::WORD_BREAK, true);
  EXPECT_STR_EQ("to Life", model.GetSelectedText());
  model.MoveCursorLeft(gfx::WORD_BREAK, true);
  model.MoveCursorLeft(gfx::WORD_BREAK, true);
  model.MoveCursorLeft(gfx::WORD_BREAK, true);  // Select to the begining.
  EXPECT_STR_EQ("The answer to Life", model.GetSelectedText());
  // Should be safe to go pervious word at the begining.
  model.MoveCursorLeft(gfx::WORD_BREAK, true);
  EXPECT_STR_EQ("The answer to Life", model.GetSelectedText());
  model.ReplaceChar('4');
  EXPECT_EQ(string16(), model.GetSelectedText());
  EXPECT_STR_EQ("42", model.GetVisibleText());
}

TEST_F(TextfieldViewsModelTest, SetText) {
  TextfieldViewsModel model(NULL);
  model.Append(ASCIIToUTF16("HELLO"));
  model.MoveCursorRight(gfx::LINE_BREAK, false);
  model.SetText(ASCIIToUTF16("GOODBYE"));
  EXPECT_STR_EQ("GOODBYE", model.GetText());
  // SetText won't reset the cursor posistion.
  EXPECT_EQ(5U, model.GetCursorPosition());
  model.SelectAll();
  EXPECT_STR_EQ("GOODBYE", model.GetSelectedText());
  model.MoveCursorRight(gfx::LINE_BREAK, false);
  EXPECT_EQ(7U, model.GetCursorPosition());

  model.SetText(ASCIIToUTF16("BYE"));
  // Setting shorter string moves the cursor to the end of the new string.
  EXPECT_EQ(3U, model.GetCursorPosition());
  EXPECT_EQ(string16(), model.GetSelectedText());
  model.SetText(ASCIIToUTF16(""));
  EXPECT_EQ(0U, model.GetCursorPosition());
}

#if defined(USE_AURA) && defined(OS_LINUX)
#define MAYBE_Clipboard DISABLED_Clipboard  // http://crbug.com/97845
#else
#define MAYBE_Clipboard Clipboard
#endif
TEST_F(TextfieldViewsModelTest, MAYBE_Clipboard) {
  ui::Clipboard* clipboard
      = views::ViewsDelegate::views_delegate->GetClipboard();
  string16 initial_clipboard_text;
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &initial_clipboard_text);
  string16 clipboard_text;
  TextfieldViewsModel model(NULL);
  model.Append(ASCIIToUTF16("HELLO WORLD"));
  model.MoveCursorRight(gfx::LINE_BREAK, false);

  // Test for cut: Empty selection.
  EXPECT_FALSE(model.Cut());
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ(UTF16ToUTF8(initial_clipboard_text), clipboard_text);
  EXPECT_STR_EQ("HELLO WORLD", model.GetText());
  EXPECT_EQ(11U, model.GetCursorPosition());

  // Test for cut: Non-empty selection.
  model.MoveCursorLeft(gfx::WORD_BREAK, true);
  EXPECT_TRUE(model.Cut());
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ("WORLD", clipboard_text);
  EXPECT_STR_EQ("HELLO ", model.GetText());
  EXPECT_EQ(6U, model.GetCursorPosition());

  // Test for copy: Empty selection.
  model.Copy();
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ("WORLD", clipboard_text);
  EXPECT_STR_EQ("HELLO ", model.GetText());
  EXPECT_EQ(6U, model.GetCursorPosition());

  // Test for copy: Non-empty selection.
  model.Append(ASCIIToUTF16("HELLO WORLD"));
  model.SelectAll();
  model.Copy();
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ("HELLO HELLO WORLD", clipboard_text);
  EXPECT_STR_EQ("HELLO HELLO WORLD", model.GetText());
  EXPECT_EQ(17U, model.GetCursorPosition());

  // Test for paste.
  model.ClearSelection();
  model.MoveCursorRight(gfx::LINE_BREAK, false);
  model.MoveCursorLeft(gfx::WORD_BREAK, true);
  EXPECT_TRUE(model.Paste());
  clipboard->ReadText(ui::Clipboard::BUFFER_STANDARD, &clipboard_text);
  EXPECT_STR_EQ("HELLO HELLO WORLD", clipboard_text);
  EXPECT_STR_EQ("HELLO HELLO HELLO HELLO WORLD", model.GetText());
  EXPECT_EQ(29U, model.GetCursorPosition());
}

void SelectWordTestVerifier(TextfieldViewsModel &model,
    const string16 &expected_selected_string, size_t expected_cursor_pos) {
  EXPECT_EQ(expected_selected_string, model.GetSelectedText());
  EXPECT_EQ(expected_cursor_pos, model.GetCursorPosition());
}

TEST_F(TextfieldViewsModelTest, SelectWordTest) {
  TextfieldViewsModel model(NULL);
  model.Append(ASCIIToUTF16("  HELLO  !!  WO     RLD "));

  // Test when cursor is at the beginning.
  model.MoveCursorLeft(gfx::LINE_BREAK, false);
  model.SelectWord();
  SelectWordTestVerifier(model, ASCIIToUTF16("  "), 2U);

  // Test when cursor is at the beginning of a word.
  gfx::SelectionModel selection(2U);
  model.MoveCursorTo(selection);
  model.SelectWord();
  SelectWordTestVerifier(model, ASCIIToUTF16("HELLO"), 7U);

  // Test when cursor is at the end of a word.
  selection = gfx::SelectionModel(15U);
  model.MoveCursorTo(selection);
  model.SelectWord();
  SelectWordTestVerifier(model, ASCIIToUTF16("     "), 20U);

  // Test when cursor is somewhere in a non-alpha-numeric fragment.
  for (size_t cursor_pos = 8; cursor_pos < 13U; cursor_pos++) {
    selection = gfx::SelectionModel(cursor_pos);
    model.MoveCursorTo(selection);
    model.SelectWord();
    SelectWordTestVerifier(model, ASCIIToUTF16("  !!  "), 13U);
  }

  // Test when cursor is somewhere in a whitespace fragment.
  selection = gfx::SelectionModel(17U);
  model.MoveCursorTo(selection);
  model.SelectWord();
  SelectWordTestVerifier(model, ASCIIToUTF16("     "), 20U);

  // Test when cursor is at the end.
  model.MoveCursorRight(gfx::LINE_BREAK, false);
  model.SelectWord();
  SelectWordTestVerifier(model, ASCIIToUTF16(" "), 24U);
}

// TODO(xji): temporarily disable in platform Win since the complex script
// characters and Chinese characters are turned into empty square due to font
// regression.
#if defined(OS_LINUX)
TEST_F(TextfieldViewsModelTest, SelectWordTest_MixScripts) {
  TextfieldViewsModel model(NULL);
  std::vector<WordAndCursor> word_and_cursor;
  word_and_cursor.push_back(WordAndCursor(L"a\x05d0", 2));
  word_and_cursor.push_back(WordAndCursor(L"a\x05d0", 2));
  word_and_cursor.push_back(WordAndCursor(L"\x05d1\x05d2", 5));
  word_and_cursor.push_back(WordAndCursor(L"\x05d1\x05d2", 5));
  word_and_cursor.push_back(WordAndCursor(L" ", 3));
  word_and_cursor.push_back(WordAndCursor(L"a\x05d0", 2));
  word_and_cursor.push_back(WordAndCursor(L"\x0915\x094d\x0915", 9));
  word_and_cursor.push_back(WordAndCursor(L"\x0915\x094d\x0915", 9));
  word_and_cursor.push_back(WordAndCursor(L" ", 10));
  word_and_cursor.push_back(WordAndCursor(L"\x4E2D\x56FD", 12));
  word_and_cursor.push_back(WordAndCursor(L"\x4E2D\x56FD", 12));
  word_and_cursor.push_back(WordAndCursor(L"\x82B1", 13));
  word_and_cursor.push_back(WordAndCursor(L"\x5929", 14));

  // The text consists of Ascii, Hebrew, Hindi with Virama sign, and Chinese.
  model.SetText(WideToUTF16(L"a\x05d0 \x05d1\x05d2 \x0915\x094d\x0915 "
                            L"\x4E2D\x56FD\x82B1\x5929"));
  for (size_t i = 0; i < word_and_cursor.size(); ++i) {
    model.MoveCursorLeft(gfx::LINE_BREAK, false);
    for (size_t j = 0; j < i; ++j)
      model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
    model.SelectWord();
    SelectWordTestVerifier(model, WideToUTF16(word_and_cursor[i].word),
                           word_and_cursor[i].cursor);
  }
}
#endif

TEST_F(TextfieldViewsModelTest, SelectionModelTest) {
  TextfieldViewsModel model(NULL);
  model.Append(ASCIIToUTF16("HELLO WORLD"));
  model.MoveCursorLeft(gfx::LINE_BREAK, false);
  gfx::SelectionModel sel;
  model.GetSelectionModel(&sel);
  EXPECT_EQ(sel.selection_start(), sel.selection_end());
  EXPECT_EQ(0U, sel.selection_start());
  EXPECT_EQ(0U, sel.selection_end());

  model.MoveCursorRight(gfx::WORD_BREAK, true);
  model.GetSelectionModel(&sel);
  EXPECT_NE(sel.selection_start(), sel.selection_end());
  EXPECT_LE(sel.selection_start(), sel.selection_end());
  EXPECT_EQ(0U, sel.selection_start());
  EXPECT_EQ(5U, sel.selection_end());

  model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
  model.GetSelectionModel(&sel);
  EXPECT_NE(sel.selection_start(), sel.selection_end());
  EXPECT_EQ(0U, sel.selection_start());
  EXPECT_EQ(4U, sel.selection_end());

  model.MoveCursorLeft(gfx::WORD_BREAK, true);
  model.GetSelectionModel(&sel);
  EXPECT_EQ(sel.selection_start(), sel.selection_end());
  EXPECT_EQ(0U, sel.selection_start());
  EXPECT_EQ(0U, sel.selection_end());

  // now from the end.
  model.MoveCursorRight(gfx::LINE_BREAK, false);
  model.GetSelectionModel(&sel);
  EXPECT_EQ(sel.selection_start(), sel.selection_end());
  EXPECT_EQ(11U, sel.selection_start());
  EXPECT_EQ(11U, sel.selection_end());

  model.MoveCursorLeft(gfx::WORD_BREAK, true);
  model.GetSelectionModel(&sel);
  EXPECT_NE(sel.selection_start(), sel.selection_end());
  EXPECT_GT(sel.selection_start(), sel.selection_end());
  EXPECT_EQ(11U, sel.selection_start());
  EXPECT_EQ(6U, sel.selection_end());

  model.MoveCursorRight(gfx::CHARACTER_BREAK, true);
  model.GetSelectionModel(&sel);
  EXPECT_NE(sel.selection_start(), sel.selection_end());
  EXPECT_GT(sel.selection_start(), sel.selection_end());
  EXPECT_EQ(11U, sel.selection_start());
  EXPECT_EQ(7U, sel.selection_end());

  model.MoveCursorRight(gfx::WORD_BREAK, true);
  model.GetSelectionModel(&sel);
  EXPECT_EQ(sel.selection_start(), sel.selection_end());
  EXPECT_EQ(11U, sel.selection_start());
  EXPECT_EQ(11U, sel.selection_end());

  // Select All
  model.MoveCursorLeft(gfx::LINE_BREAK, true);
  model.GetSelectionModel(&sel);
  EXPECT_NE(sel.selection_start(), sel.selection_end());
  EXPECT_GT(sel.selection_start(), sel.selection_end());
  EXPECT_EQ(11U, sel.selection_start());
  EXPECT_EQ(0U, sel.selection_end());
}

TEST_F(TextfieldViewsModelTest, SelectSelectionModelTest) {
  TextfieldViewsModel model(NULL);
  model.Append(ASCIIToUTF16("HELLO WORLD"));
  model.SelectSelectionModel(gfx::SelectionModel(0, 6));
  EXPECT_STR_EQ("HELLO ", model.GetSelectedText());

  model.SelectSelectionModel(gfx::SelectionModel(6, 1));
  EXPECT_STR_EQ("ELLO ", model.GetSelectedText());

  model.SelectSelectionModel(gfx::SelectionModel(2, 1000));
  EXPECT_STR_EQ("LLO WORLD", model.GetSelectedText());

  model.SelectSelectionModel(gfx::SelectionModel(1000, 3));
  EXPECT_STR_EQ("LO WORLD", model.GetSelectedText());

  model.SelectSelectionModel(gfx::SelectionModel(0, 0));
  EXPECT_TRUE(model.GetSelectedText().empty());

  model.SelectSelectionModel(gfx::SelectionModel(3, 3));
  EXPECT_TRUE(model.GetSelectedText().empty());

  model.SelectSelectionModel(gfx::SelectionModel(1000, 100));
  EXPECT_TRUE(model.GetSelectedText().empty());

  model.SelectSelectionModel(gfx::SelectionModel(1000, 1000));
  EXPECT_TRUE(model.GetSelectedText().empty());
}

TEST_F(TextfieldViewsModelTest, CompositionTextTest) {
  TextfieldViewsModel model(this);
  model.Append(ASCIIToUTF16("1234590"));
  model.SelectSelectionModel(gfx::SelectionModel(5, 5));
  EXPECT_FALSE(model.HasSelection());
  EXPECT_EQ(5U, model.GetCursorPosition());

  ui::Range range;
  model.GetTextRange(&range);
  EXPECT_EQ(0U, range.start());
  EXPECT_EQ(7U, range.end());

  ui::CompositionText composition;
  composition.text = ASCIIToUTF16("678");
  composition.underlines.push_back(ui::CompositionUnderline(0, 3, 0, false));
  composition.selection = ui::Range(2, 3);
  model.SetCompositionText(composition);
  EXPECT_TRUE(model.HasCompositionText());
  EXPECT_TRUE(model.HasSelection());

  model.GetTextRange(&range);
  EXPECT_EQ(10U, range.end());
  EXPECT_STR_EQ("1234567890", model.GetText());

  model.GetCompositionTextRange(&range);
  EXPECT_EQ(5U, range.start());
  EXPECT_EQ(8U, range.end());
  // composition text
  EXPECT_STR_EQ("456", model.GetTextFromRange(ui::Range(3, 6)));

  gfx::SelectionModel selection;
  model.GetSelectionModel(&selection);
  EXPECT_EQ(7U, selection.selection_start());
  EXPECT_EQ(8U, selection.selection_end());
  EXPECT_STR_EQ("8", model.GetSelectedText());

  EXPECT_FALSE(composition_text_confirmed_or_cleared_);
  model.CancelCompositionText();
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_FALSE(model.HasCompositionText());
  EXPECT_FALSE(model.HasSelection());
  EXPECT_EQ(5U, model.GetCursorPosition());

  model.SetCompositionText(composition);
  EXPECT_STR_EQ("1234567890", model.GetText());
  EXPECT_TRUE(model.SetText(ASCIIToUTF16("1234567890")));
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  model.MoveCursorRight(gfx::LINE_BREAK, false);

  model.SetCompositionText(composition);
  EXPECT_STR_EQ("1234567890678", model.GetText());

  model.InsertText(UTF8ToUTF16("-"));
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_STR_EQ("1234567890-", model.GetText());
  EXPECT_FALSE(model.HasCompositionText());
  EXPECT_FALSE(model.HasSelection());

  model.MoveCursorLeft(gfx::CHARACTER_BREAK, true);
  EXPECT_STR_EQ("-", model.GetSelectedText());
  model.SetCompositionText(composition);
  EXPECT_STR_EQ("1234567890678", model.GetText());

  model.ReplaceText(UTF8ToUTF16("-"));
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_STR_EQ("1234567890-", model.GetText());
  EXPECT_FALSE(model.HasCompositionText());
  EXPECT_FALSE(model.HasSelection());

  model.SetCompositionText(composition);
  model.Append(UTF8ToUTF16("-"));
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_STR_EQ("1234567890-678-", model.GetText());

  model.SetCompositionText(composition);
  model.Delete();
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_STR_EQ("1234567890-678-", model.GetText());

  model.SetCompositionText(composition);
  model.Backspace();
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_STR_EQ("1234567890-678-", model.GetText());

  model.SetText(string16());
  model.SetCompositionText(composition);
  model.MoveCursorLeft(gfx::CHARACTER_BREAK, false);
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_STR_EQ("678", model.GetText());
  EXPECT_EQ(2U, model.GetCursorPosition());

  model.SetCompositionText(composition);
  model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_STR_EQ("676788", model.GetText());
  EXPECT_EQ(6U, model.GetCursorPosition());

  model.SetCompositionText(composition);
  model.MoveCursorLeft(gfx::WORD_BREAK, false);
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_STR_EQ("676788678", model.GetText());

  model.SetText(string16());
  model.SetCompositionText(composition);
  model.MoveCursorRight(gfx::WORD_BREAK, false);
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;

  model.SetCompositionText(composition);
  model.MoveCursorLeft(gfx::LINE_BREAK, true);
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_STR_EQ("678678", model.GetText());

  model.SetCompositionText(composition);
  model.MoveCursorRight(gfx::LINE_BREAK, false);
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_STR_EQ("678", model.GetText());

  model.SetCompositionText(composition);
  gfx::SelectionModel sel(0);
  sel.set_selection_start(model.render_text()->GetSelectionStart());
  model.MoveCursorTo(sel);
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_STR_EQ("678678", model.GetText());

  model.SetCompositionText(composition);
  model.SelectSelectionModel(gfx::SelectionModel(0, 3));
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_STR_EQ("678", model.GetText());

  model.SetCompositionText(composition);
  model.SelectAll();
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_STR_EQ("678", model.GetText());

  model.SetCompositionText(composition);
  model.SelectWord();
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;
  EXPECT_STR_EQ("678", model.GetText());

  model.SetCompositionText(composition);
  model.ClearSelection();
  EXPECT_TRUE(composition_text_confirmed_or_cleared_);
  composition_text_confirmed_or_cleared_ = false;

  model.SetCompositionText(composition);
  EXPECT_FALSE(model.Cut());
  EXPECT_FALSE(composition_text_confirmed_or_cleared_);
}

TEST_F(TextfieldViewsModelTest, UndoRedo_BasicTest) {
  TextfieldViewsModel model(NULL);
  model.InsertChar('a');
  EXPECT_FALSE(model.Redo());  // nothing to redo
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("a", model.GetText());

  // Continuous inserts are treated as one edit.
  model.InsertChar('b');
  model.InsertChar('c');
  EXPECT_STR_EQ("abc", model.GetText());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("a", model.GetText());
  EXPECT_EQ(1U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_EQ(0U, model.GetCursorPosition());

  // Undoing further shouldn't change the text.
  EXPECT_FALSE(model.Undo());
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_FALSE(model.Undo());
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_EQ(0U, model.GetCursorPosition());

  // Redoing to the latest text.
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("a", model.GetText());
  EXPECT_EQ(1U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("abc", model.GetText());
  EXPECT_EQ(3U, model.GetCursorPosition());

  // Backspace ===============================
  EXPECT_TRUE(model.Backspace());
  EXPECT_STR_EQ("ab", model.GetText());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("abc", model.GetText());
  EXPECT_EQ(3U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("ab", model.GetText());
  EXPECT_EQ(2U, model.GetCursorPosition());
  // Continous backspaces are treated as one edit.
  EXPECT_TRUE(model.Backspace());
  EXPECT_TRUE(model.Backspace());
  EXPECT_STR_EQ("", model.GetText());
  // Extra backspace shouldn't affect the history.
  EXPECT_FALSE(model.Backspace());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ab", model.GetText());
  EXPECT_EQ(2U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("abc", model.GetText());
  EXPECT_EQ(3U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("a", model.GetText());
  EXPECT_EQ(1U, model.GetCursorPosition());

  // Clear history
  model.ClearEditHistory();
  EXPECT_FALSE(model.Undo());
  EXPECT_FALSE(model.Redo());
  EXPECT_STR_EQ("a", model.GetText());
  EXPECT_EQ(1U, model.GetCursorPosition());

  // Delete ===============================
  model.SetText(ASCIIToUTF16("ABCDE"));
  model.ClearEditHistory();
  gfx::SelectionModel sel(2);
  model.MoveCursorTo(sel);
  EXPECT_TRUE(model.Delete());
  EXPECT_STR_EQ("ABDE", model.GetText());
  model.MoveCursorLeft(gfx::LINE_BREAK, false);
  EXPECT_TRUE(model.Delete());
  EXPECT_STR_EQ("BDE", model.GetText());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ABDE", model.GetText());
  EXPECT_EQ(0U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ABCDE", model.GetText());
  EXPECT_EQ(2U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("ABDE", model.GetText());
  EXPECT_EQ(2U, model.GetCursorPosition());
  // Continous deletes are treated as one edit.
  EXPECT_TRUE(model.Delete());
  EXPECT_TRUE(model.Delete());
  EXPECT_STR_EQ("AB", model.GetText());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ABDE", model.GetText());
  EXPECT_EQ(2U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("AB", model.GetText());
  EXPECT_EQ(2U, model.GetCursorPosition());
}

TEST_F(TextfieldViewsModelTest, UndoRedo_SetText) {
  // This is to test the undo/redo behavior of omnibox.
  TextfieldViewsModel model(NULL);
  model.InsertChar('w');
  EXPECT_STR_EQ("w", model.GetText());
  EXPECT_EQ(1U, model.GetCursorPosition());
  model.SetText(ASCIIToUTF16("www.google.com"));
  EXPECT_EQ(1U, model.GetCursorPosition());
  EXPECT_STR_EQ("www.google.com", model.GetText());
  model.SelectSelectionModel(gfx::SelectionModel(14, 1));
  model.InsertChar('w');
  EXPECT_STR_EQ("ww", model.GetText());
  model.SetText(ASCIIToUTF16("www.google.com"));
  model.SelectSelectionModel(gfx::SelectionModel(14, 2));
  model.InsertChar('w');
  EXPECT_STR_EQ("www", model.GetText());
  model.SetText(ASCIIToUTF16("www.google.com"));
  model.SelectSelectionModel(gfx::SelectionModel(14, 3));
  model.InsertChar('.');
  EXPECT_STR_EQ("www.", model.GetText());
  model.SetText(ASCIIToUTF16("www.google.com"));
  model.SelectSelectionModel(gfx::SelectionModel(14, 4));
  model.InsertChar('y');
  EXPECT_STR_EQ("www.y", model.GetText());
  model.SetText(ASCIIToUTF16("www.youtube.com"));
  EXPECT_STR_EQ("www.youtube.com", model.GetText());
  EXPECT_EQ(5U, model.GetCursorPosition());

  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("www.google.com", model.GetText());
  EXPECT_EQ(4U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("www.google.com", model.GetText());
  EXPECT_EQ(3U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("www.google.com", model.GetText());
  EXPECT_EQ(2U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("www.google.com", model.GetText());
  EXPECT_EQ(1U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_EQ(0U, model.GetCursorPosition());
  EXPECT_FALSE(model.Undo());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("www.google.com", model.GetText());
  EXPECT_EQ(1U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("www.google.com", model.GetText());
  EXPECT_EQ(2U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("www.google.com", model.GetText());
  EXPECT_EQ(3U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("www.google.com", model.GetText());
  EXPECT_EQ(4U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("www.youtube.com", model.GetText());
  EXPECT_EQ(5U, model.GetCursorPosition());
  EXPECT_FALSE(model.Redo());
}

#if defined(USE_AURA) && defined(OS_LINUX)
// This can be re-enabled when aura on linux has clipboard support.
// http://crbug.com/97845
#define MAYBE_UndoRedo_CutCopyPasteTest DISABLED_UndoRedo_CutCopyPasteTest
#else
#define MAYBE_UndoRedo_CutCopyPasteTest UndoRedo_CutCopyPasteTest
#endif
TEST_F(TextfieldViewsModelTest, MAYBE_UndoRedo_CutCopyPasteTest) {
  TextfieldViewsModel model(NULL);
  model.SetText(ASCIIToUTF16("ABCDE"));
  EXPECT_FALSE(model.Redo());  // nothing to redo
  // Cut
  model.MoveCursorTo(gfx::SelectionModel(1, 3));
  model.Cut();
  EXPECT_STR_EQ("ADE", model.GetText());
  EXPECT_EQ(1U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ABCDE", model.GetText());
  EXPECT_EQ(3U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_EQ(0U, model.GetCursorPosition());
  EXPECT_FALSE(model.Undo());  // no more undo
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("ABCDE", model.GetText());
  EXPECT_EQ(0U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("ADE", model.GetText());
  EXPECT_EQ(1U, model.GetCursorPosition());
  EXPECT_FALSE(model.Redo());  // no more redo
  EXPECT_STR_EQ("ADE", model.GetText());

  model.Paste();
  model.Paste();
  model.Paste();
  EXPECT_STR_EQ("ABCBCBCDE", model.GetText());
  EXPECT_EQ(7U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ABCBCDE", model.GetText());
  EXPECT_EQ(5U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ABCDE", model.GetText());
  EXPECT_EQ(3U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ADE", model.GetText());
  EXPECT_EQ(1U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ABCDE", model.GetText());
  EXPECT_EQ(3U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_EQ(0U, model.GetCursorPosition());
  EXPECT_FALSE(model.Undo());
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("ABCDE", model.GetText());  // Redoing SetText
  EXPECT_EQ(0U, model.GetCursorPosition());

  // Redo
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("ADE", model.GetText());
  EXPECT_EQ(1U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("ABCDE", model.GetText());
  EXPECT_EQ(3U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("ABCBCDE", model.GetText());
  EXPECT_EQ(5U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("ABCBCBCDE", model.GetText());
  EXPECT_EQ(7U, model.GetCursorPosition());
  EXPECT_FALSE(model.Redo());

  // with SelectSelectionModel
  model.SelectSelectionModel(gfx::SelectionModel(1, 3));
  EXPECT_TRUE(model.Cut());
  EXPECT_STR_EQ("ABCBCDE", model.GetText());
  EXPECT_EQ(1U, model.GetCursorPosition());
  model.SelectSelectionModel(gfx::SelectionModel(1, 1));
  EXPECT_FALSE(model.Cut());
  model.MoveCursorRight(gfx::LINE_BREAK, false);
  EXPECT_TRUE(model.Paste());
  EXPECT_STR_EQ("ABCBCDEBC", model.GetText());
  EXPECT_EQ(9U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ABCBCDE", model.GetText());
  EXPECT_EQ(7U, model.GetCursorPosition());
  // empty cut shouldn't create an edit.
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ABCBCBCDE", model.GetText());
  EXPECT_EQ(3U, model.GetCursorPosition());

  // Copy
  ResetModel(&model);
  model.SetText(ASCIIToUTF16("12345"));
  EXPECT_STR_EQ("12345", model.GetText());
  EXPECT_EQ(0U, model.GetCursorPosition());
  model.MoveCursorTo(gfx::SelectionModel(1, 3));
  model.Copy();  // Copy "23"
  EXPECT_STR_EQ("12345", model.GetText());
  EXPECT_EQ(3U, model.GetCursorPosition());
  model.Paste();  // Paste "23" into "23".
  EXPECT_STR_EQ("12345", model.GetText());
  EXPECT_EQ(3U, model.GetCursorPosition());
  model.Paste();
  EXPECT_STR_EQ("1232345", model.GetText());
  EXPECT_EQ(5U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("12345", model.GetText());
  EXPECT_EQ(3U, model.GetCursorPosition());
  // TODO(oshima): We need to change the return type from bool to enum.
  EXPECT_FALSE(model.Undo());  // No text change.
  EXPECT_STR_EQ("12345", model.GetText());
  EXPECT_EQ(3U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_FALSE(model.Undo());
  // Redo
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("12345", model.GetText());
  EXPECT_EQ(0U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("12345", model.GetText());  // For 1st paste
  EXPECT_EQ(3U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("1232345", model.GetText());
  EXPECT_EQ(5U, model.GetCursorPosition());
  EXPECT_FALSE(model.Redo());
  EXPECT_STR_EQ("1232345", model.GetText());

  // Test using SelectSelectionModel
  model.SelectSelectionModel(gfx::SelectionModel(1, 3));
  model.Copy();
  EXPECT_STR_EQ("1232345", model.GetText());
  model.MoveCursorRight(gfx::LINE_BREAK, false);
  EXPECT_TRUE(model.Paste());
  EXPECT_STR_EQ("123234523", model.GetText());
  EXPECT_EQ(9U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("1232345", model.GetText());
  EXPECT_EQ(7U, model.GetCursorPosition());
}

TEST_F(TextfieldViewsModelTest, UndoRedo_CursorTest) {
  TextfieldViewsModel model(NULL);
  model.InsertChar('a');
  model.MoveCursorLeft(gfx::CHARACTER_BREAK, false);
  model.MoveCursorRight(gfx::CHARACTER_BREAK, false);
  model.InsertChar('b');
  // Moving the cursor shouldn't create a new edit.
  EXPECT_STR_EQ("ab", model.GetText());
  EXPECT_FALSE(model.Redo());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_FALSE(model.Undo());
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("ab", model.GetText());
  EXPECT_EQ(2U, model.GetCursorPosition());
  EXPECT_FALSE(model.Redo());
}

void RunInsertReplaceTest(TextfieldViewsModel& model) {
  gfx::SelectionModel sel;
  model.GetSelectionModel(&sel);
  bool reverse = (sel.selection_start() > sel.selection_end());

  model.InsertChar('1');
  model.InsertChar('2');
  model.InsertChar('3');
  EXPECT_STR_EQ("a123d", model.GetText());
  EXPECT_EQ(4U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("abcd", model.GetText());
  EXPECT_EQ(reverse ? 1U : 3U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_EQ(0U, model.GetCursorPosition());
  EXPECT_FALSE(model.Undo());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("abcd", model.GetText());
  EXPECT_EQ(0U, model.GetCursorPosition());  // By SetText
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("a123d", model.GetText());
  EXPECT_EQ(4U, model.GetCursorPosition());
  EXPECT_FALSE(model.Redo());
}

void RunOverwriteReplaceTest(TextfieldViewsModel& model) {
  gfx::SelectionModel sel;
  model.GetSelectionModel(&sel);
  bool reverse = (sel.selection_start() > sel.selection_end());

  model.ReplaceChar('1');
  model.ReplaceChar('2');
  model.ReplaceChar('3');
  model.ReplaceChar('4');
  EXPECT_STR_EQ("a1234", model.GetText());
  EXPECT_EQ(5U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("abcd", model.GetText());
  EXPECT_EQ(reverse ? 1U : 3U, model.GetCursorPosition());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_EQ(0U, model.GetCursorPosition());
  EXPECT_FALSE(model.Undo());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("abcd", model.GetText());
  EXPECT_EQ(0U, model.GetCursorPosition());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("a1234", model.GetText());
  EXPECT_EQ(5U, model.GetCursorPosition());
  EXPECT_FALSE(model.Redo());
}

TEST_F(TextfieldViewsModelTest, UndoRedo_ReplaceTest) {
  // By Cursor
  {
    SCOPED_TRACE("forward & insert by cursor");
    TextfieldViewsModel model(NULL);
    model.SetText(ASCIIToUTF16("abcd"));
    model.MoveCursorTo(gfx::SelectionModel(1, 3));
    RunInsertReplaceTest(model);
  }
  {
    SCOPED_TRACE("backward & insert by cursor");
    TextfieldViewsModel model(NULL);
    model.SetText(ASCIIToUTF16("abcd"));
    model.MoveCursorTo(gfx::SelectionModel(3, 1));
    RunInsertReplaceTest(model);
  }
  {
    SCOPED_TRACE("forward & overwrite by cursor");
    TextfieldViewsModel model(NULL);
    model.SetText(ASCIIToUTF16("abcd"));
    model.MoveCursorTo(gfx::SelectionModel(1, 3));
    RunOverwriteReplaceTest(model);
  }
  {
    SCOPED_TRACE("backward & overwrite by cursor");
    TextfieldViewsModel model(NULL);
    model.SetText(ASCIIToUTF16("abcd"));
    model.MoveCursorTo(gfx::SelectionModel(3, 1));
    RunOverwriteReplaceTest(model);
  }
  // By SelectSelectionModel API
  {
    SCOPED_TRACE("forward & insert by SelectSelectionModel");
    TextfieldViewsModel model(NULL);
    model.SetText(ASCIIToUTF16("abcd"));
    model.SelectSelectionModel(gfx::SelectionModel(1, 3));
    RunInsertReplaceTest(model);
  }
  {
    SCOPED_TRACE("backward & insert by SelectSelectionModel");
    TextfieldViewsModel model(NULL);
    model.SetText(ASCIIToUTF16("abcd"));
    model.SelectSelectionModel(gfx::SelectionModel(3, 1));
    RunInsertReplaceTest(model);
  }
  {
    SCOPED_TRACE("forward & overwrite by SelectSelectionModel");
    TextfieldViewsModel model(NULL);
    model.SetText(ASCIIToUTF16("abcd"));
    model.SelectSelectionModel(gfx::SelectionModel(1, 3));
    RunOverwriteReplaceTest(model);
  }
  {
    SCOPED_TRACE("backward & overwrite by SelectSelectionModel");
    TextfieldViewsModel model(NULL);
    model.SetText(ASCIIToUTF16("abcd"));
    model.SelectSelectionModel(gfx::SelectionModel(3, 1));
    RunOverwriteReplaceTest(model);
  }
}

TEST_F(TextfieldViewsModelTest, UndoRedo_CompositionText) {
  TextfieldViewsModel model(NULL);

  ui::CompositionText composition;
  composition.text = ASCIIToUTF16("abc");
  composition.underlines.push_back(ui::CompositionUnderline(0, 3, 0, false));
  composition.selection = ui::Range(2, 3);

  model.SetText(ASCIIToUTF16("ABCDE"));
  model.MoveCursorRight(gfx::LINE_BREAK, false);
  model.InsertChar('x');
  EXPECT_STR_EQ("ABCDEx", model.GetText());
  EXPECT_TRUE(model.Undo());  // set composition should forget undone edit.
  model.SetCompositionText(composition);
  EXPECT_TRUE(model.HasCompositionText());
  EXPECT_TRUE(model.HasSelection());
  EXPECT_STR_EQ("ABCDEabc", model.GetText());

  // Accepting composition
  model.ConfirmCompositionText();
  EXPECT_STR_EQ("ABCDEabc", model.GetText());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ABCDE", model.GetText());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("", model.GetText());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("ABCDE", model.GetText());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("ABCDEabc", model.GetText());
  EXPECT_FALSE(model.Redo());

  // Canceling composition
  model.MoveCursorLeft(gfx::LINE_BREAK, false);
  model.SetCompositionText(composition);
  EXPECT_STR_EQ("abcABCDEabc", model.GetText());
  model.CancelCompositionText();
  EXPECT_STR_EQ("ABCDEabc", model.GetText());
  EXPECT_FALSE(model.Redo());
  EXPECT_STR_EQ("ABCDEabc", model.GetText());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ABCDE", model.GetText());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("ABCDEabc", model.GetText());
  EXPECT_FALSE(model.Redo());

  // SetText with the same text as the result.
  ResetModel(&model);
  model.SetText(ASCIIToUTF16("ABCDE"));
  model.MoveCursorRight(gfx::LINE_BREAK, false);
  model.SetCompositionText(composition);
  EXPECT_STR_EQ("ABCDEabc", model.GetText());
  model.SetText(ASCIIToUTF16("ABCDEabc"));
  EXPECT_STR_EQ("ABCDEabc", model.GetText());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ABCDE", model.GetText());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("ABCDEabc", model.GetText());
  EXPECT_FALSE(model.Redo());

  // SetText with the different text than the result should not
  // remember composition text.
  ResetModel(&model);
  model.SetText(ASCIIToUTF16("ABCDE"));
  model.MoveCursorRight(gfx::LINE_BREAK, false);
  model.SetCompositionText(composition);
  EXPECT_STR_EQ("ABCDEabc", model.GetText());
  model.SetText(ASCIIToUTF16("1234"));
  EXPECT_STR_EQ("1234", model.GetText());
  EXPECT_TRUE(model.Undo());
  EXPECT_STR_EQ("ABCDE", model.GetText());
  EXPECT_TRUE(model.Redo());
  EXPECT_STR_EQ("1234", model.GetText());
  EXPECT_FALSE(model.Redo());

  // TODO(oshima): We need MockInputMethod to test the behavior with IME.
}

}  // namespace views