Chromium Code Reviews| Index: ui/base/win/tsf_text_store_unittest.cc |
| diff --git a/ui/base/win/tsf_text_store_unittest.cc b/ui/base/win/tsf_text_store_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..a1b39f16ace2510f3b937872d035a291c0986e22 |
| --- /dev/null |
| +++ b/ui/base/win/tsf_text_store_unittest.cc |
| @@ -0,0 +1,466 @@ |
| +// Copyright (c) 2012 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/win/tsf_text_store.h" |
| + |
| +#include "base/win/scoped_com_initializer.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| +#include "testing/gmock/include/gmock/gmock.h" |
| +#include "ui/base/ime/text_input_client.h" |
| +#include "ui/gfx/rect.h" |
| + |
| +using testing::_; |
| +using testing::Invoke; |
| +using testing::Return; |
| + |
| +namespace ui { |
| + |
| +namespace { |
| +class MockTextInputClient : public TextInputClient { |
| + public: |
| + ~MockTextInputClient() {} |
| + MOCK_METHOD1(SetCompositionText, void(const ui::CompositionText&)); |
| + MOCK_METHOD0(ConfirmCompositionText, void()); |
| + MOCK_METHOD0(ClearCompositionText, void()); |
| + MOCK_METHOD1(InsertText, void(const string16&)); |
| + MOCK_METHOD2(InsertChar, void(char16, int)); |
| + MOCK_CONST_METHOD0(GetTextInputType, ui::TextInputType()); |
| + MOCK_CONST_METHOD0(CanComposeInline, bool()); |
| + MOCK_METHOD0(GetCaretBounds, gfx::Rect()); |
| + MOCK_METHOD2(GetCompositionCharacterBounds, bool(uint32, gfx::Rect*)); |
| + MOCK_METHOD0(HasCompositionText, bool()); |
| + MOCK_METHOD1(GetTextRange, bool(ui::Range*)); |
| + MOCK_METHOD1(GetCompositionTextRange, bool(ui::Range*)); |
| + MOCK_METHOD1(GetSelectionRange, bool(ui::Range*)); |
| + MOCK_METHOD1(SetSelectionRange, bool(const ui::Range&)); |
| + MOCK_METHOD1(DeleteRange, bool(const ui::Range&)); |
| + MOCK_METHOD2(GetTextFromRange, bool(const ui::Range&, string16*)); |
| + MOCK_METHOD0(OnInputMethodChanged, void()); |
| + MOCK_METHOD1(ChangeTextDirectionAndLayoutAlignment, |
| + bool(base::i18n::TextDirection)); |
| +}; |
| + |
| +class MockStoreACPSink : public ITextStoreACPSink { |
| + public: |
| + MockStoreACPSink() : ref_count_(0) { |
| + } |
| + |
| + // IUnknown |
| + virtual ULONG STDMETHODCALLTYPE AddRef() OVERRIDE { |
| + return InterlockedIncrement(&ref_count_); |
| + } |
| + virtual ULONG STDMETHODCALLTYPE Release() OVERRIDE { |
| + const LONG count = InterlockedDecrement(&ref_count_); |
| + if (!count) { |
| + delete this; |
| + return 0; |
| + } |
| + return static_cast<ULONG>(count); |
| + } |
| + virtual HRESULT STDMETHODCALLTYPE QueryInterface( |
| + REFIID iid, void** report) OVERRIDE { |
| + if (iid == IID_IUnknown || iid == IID_ITextStoreACPSink) { |
| + *report = static_cast<ITextStoreACPSink*>(this); |
| + } else { |
| + *report = NULL; |
| + return E_NOINTERFACE; |
| + } |
| + AddRef(); |
| + return S_OK; |
| + } |
| + |
| + // ITextStoreACPSink |
| + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, OnTextChange, |
| + HRESULT(DWORD, const TS_TEXTCHANGE*)); |
| + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, OnSelectionChange, |
| + HRESULT()); |
| + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, OnLayoutChange, |
| + HRESULT(TsLayoutCode, TsViewCookie)); |
| + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, OnStatusChange, |
| + HRESULT(DWORD)); |
| + MOCK_METHOD4_WITH_CALLTYPE(STDMETHODCALLTYPE, OnAttrsChange, |
| + HRESULT(LONG, LONG, ULONG, const TS_ATTRID*)); |
| + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, OnLockGranted, |
| + HRESULT(DWORD)); |
| + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, OnStartEditTransaction, |
| + HRESULT()); |
| + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, OnEndEditTransaction, |
| + HRESULT()); |
| + |
| + private: |
| + ~MockStoreACPSink() { |
| + } |
| + volatile LONG ref_count_; |
| +}; |
| + |
| +} // namespace |
| + |
| +class TsfTextStoreTest : public testing::Test { |
| + protected: |
| + virtual void SetUp() OVERRIDE { |
| + text_store_ = new TsfTextStore(); |
| + text_store_->AddRef(); |
| + sink_ = new MockStoreACPSink(); |
| + sink_->AddRef(); |
| + EXPECT_EQ(S_OK, |
| + text_store_->AdviseSink(IID_ITextStoreACPSink, |
|
Yohei Yukawa
2012/08/23 10:16:44
Indent?
horo
2012/08/23 10:36:18
Done.
|
| + sink_, TS_AS_ALL_SINKS)); |
| + text_store_->SetFocusedTextInputClient(0, &text_input_client_); |
| + } |
| + |
| + virtual void TearDown() OVERRIDE { |
| + EXPECT_EQ(S_OK, text_store_->UnadviseSink(sink_)); |
| + sink_->Release(); |
| + text_store_->Release(); |
| + } |
| + |
| + base::win::ScopedCOMInitializer com_initializer_; |
| + MockTextInputClient text_input_client_; |
| + TsfTextStore* text_store_; |
| + MockStoreACPSink* sink_; |
| +}; |
| + |
| +class TsfTextStoreTestCallback { |
| + public: |
| + explicit TsfTextStoreTestCallback(TsfTextStore* text_store) |
| + : text_store_(text_store) { |
| + CHECK(text_store_); |
| + } |
| + virtual ~TsfTextStoreTestCallback() {} |
| + |
| + protected: |
| + // Accessros to the internal state of TsfTextStore. |
| + bool* edit_flag() { return &text_store_->edit_flag_; } |
| + string16* string_buffer() { return &text_store_->string_buffer_; } |
| + size_t* committed_size() { return &text_store_->committed_size_; } |
| + Range* selection() { return &text_store_->selection_; } |
| + CompositionUnderlines* composition_undelines() { |
| + return &text_store_->composition_undelines_; |
| + } |
| + |
| + TsfTextStore* text_store_; |
| +}; |
| + |
| +class SelectionTestCallback : public TsfTextStoreTestCallback { |
| + public: |
| + explicit SelectionTestCallback(TsfTextStore* text_store) |
| + : TsfTextStoreTestCallback(text_store) { |
| + } |
| + |
| + HRESULT ReadLockGranted(DWORD flags) { |
| + *string_buffer() = L""; |
| + *committed_size() = 0; |
| + selection()->set_start(0); |
| + selection()->set_end(0); |
| + |
| + GetSelectionTest(0, 0); |
| + SetSelectionTest(0, 0, TF_E_NOLOCK); |
| + |
| + *string_buffer() = L"012345"; |
| + *committed_size() = 0; |
| + selection()->set_start(0); |
| + selection()->set_end(3); |
| + |
| + GetSelectionTest(0, 3); |
| + SetSelectionTest(0, 0, TF_E_NOLOCK); |
| + |
| + return S_OK; |
| + } |
| + |
| + HRESULT ReadWriteLockGranted(DWORD flags) { |
| + *string_buffer() = L""; |
| + *committed_size() = 0; |
| + selection()->set_start(0); |
| + selection()->set_end(0); |
| + |
| + SetSelectionTest(0, 0, S_OK); |
| + GetSelectionTest(0, 0); |
| + SetSelectionTest(0, 1, TF_E_INVALIDPOS); |
| + GetSelectionTest(0, 0); |
| + SetSelectionTest(1, 0, TF_E_INVALIDPOS); |
| + GetSelectionTest(0, 0); |
| + SetSelectionTest(1, 1, TF_E_INVALIDPOS); |
| + GetSelectionTest(0, 0); |
| + |
| + *string_buffer() = L"012345"; |
| + *committed_size() = 0; |
| + selection()->set_start(0); |
| + selection()->set_end(0); |
| + |
| + SetSelectionTest(0, 0, S_OK); |
| + GetSelectionTest(0, 0); |
| + SetSelectionTest(0, 3, S_OK); |
| + GetSelectionTest(0, 3); |
| + SetSelectionTest(0, 6, S_OK); |
| + GetSelectionTest(0, 6); |
| + SetSelectionTest(0, 7, TF_E_INVALIDPOS); |
| + GetSelectionTest(0, 6); |
| + |
| + SetSelectionTest(3, 0, TF_E_INVALIDPOS); |
| + GetSelectionTest(0, 6); |
| + SetSelectionTest(3, 3, S_OK); |
| + GetSelectionTest(3, 3); |
| + SetSelectionTest(3, 6, S_OK); |
| + GetSelectionTest(3, 6); |
| + SetSelectionTest(3, 7, TF_E_INVALIDPOS); |
| + GetSelectionTest(3, 6); |
| + |
| + SetSelectionTest(6, 0, TF_E_INVALIDPOS); |
| + GetSelectionTest(3, 6); |
| + SetSelectionTest(6, 3, TF_E_INVALIDPOS); |
| + GetSelectionTest(3, 6); |
| + SetSelectionTest(6, 6, S_OK); |
| + GetSelectionTest(6, 6); |
| + SetSelectionTest(6, 7, TF_E_INVALIDPOS); |
| + GetSelectionTest(6, 6); |
| + |
| + return S_OK; |
| + } |
| + |
| + private: |
| + void SetSelectionTest(LONG acp_start, LONG acp_end, HRESULT expected_result) { |
| + TS_SELECTION_ACP selection; |
| + selection.acpStart = acp_start; |
| + selection.acpEnd = acp_end; |
| + selection.style.ase = TS_AE_NONE; |
| + selection.style.fInterimChar = 0; |
| + EXPECT_EQ(expected_result, text_store_->SetSelection(1, &selection)); |
| + } |
| + |
| + void GetSelectionTest(LONG expected_acp_start, LONG expected_acp_end) { |
| + TS_SELECTION_ACP selection; |
| + ULONG fetched; |
| + EXPECT_EQ(S_OK, text_store_->GetSelection(0, 1, &selection, &fetched)); |
| + EXPECT_EQ(1, fetched); |
| + EXPECT_EQ(expected_acp_start, selection.acpStart); |
| + EXPECT_EQ(expected_acp_end, selection.acpEnd); |
| + } |
| +}; |
| + |
| +TEST_F(TsfTextStoreTest, GetStatus) { |
| + TS_STATUS status; |
| + EXPECT_EQ(S_OK, text_store_->GetStatus(&status)); |
| + EXPECT_EQ(0, status.dwDynamicFlags); |
| + EXPECT_EQ(TS_SS_NOHIDDENTEXT, status.dwStaticFlags); |
| +} |
| + |
| +TEST_F(TsfTextStoreTest, SetGetSelectionTest) { |
| + SelectionTestCallback callback(text_store_); |
| + EXPECT_CALL(*sink_, OnLockGranted(_)) |
| + .WillOnce(Invoke(&callback, &SelectionTestCallback::ReadLockGranted)) |
| + .WillOnce(Invoke(&callback, |
| + &SelectionTestCallback::ReadWriteLockGranted)); |
| + |
| + TS_SELECTION_ACP selection_buffer; |
| + ULONG fetched_count; |
| + EXPECT_EQ(TS_E_NOLOCK, |
| + text_store_->GetSelection(0, 1, &selection_buffer, |
| + &fetched_count)); |
| + |
| + HRESULT result; |
| + EXPECT_EQ(S_OK, |
| + text_store_->RequestLock(TS_LF_READ, &result)); |
| + EXPECT_EQ(S_OK, |
| + text_store_->RequestLock(TS_LF_READWRITE, &result)); |
| +} |
| + |
| +class SenarioTestCallback : public TsfTextStoreTestCallback { |
| + public: |
| + explicit SenarioTestCallback(TsfTextStore* text_store) |
| + : TsfTextStoreTestCallback(text_store) { |
| + } |
| + |
| + HRESULT LockGranted1(DWORD flags) { |
| + TS_SELECTION_ACP selection; |
| + selection.acpStart = 0; |
| + selection.acpEnd = 0; |
| + selection.style.ase = TS_AE_NONE; |
| + selection.style.fInterimChar = 0; |
| + EXPECT_EQ(S_OK, |
| + text_store_->SetSelection(1, &selection)); |
| + TS_TEXTCHANGE change; |
| + EXPECT_EQ(S_OK, |
| + text_store_->SetText(0, 0, 0, L"abc", 3, &change)); |
| + EXPECT_EQ(0, change.acpStart); |
| + EXPECT_EQ(0, change.acpOldEnd); |
| + EXPECT_EQ(3, change.acpNewEnd); |
| + |
| + EXPECT_EQ(S_OK, |
| + text_store_->SetText(0, 1, 2, L"xyz", 3, &change)); |
| + EXPECT_EQ(1, change.acpStart); |
| + EXPECT_EQ(2, change.acpOldEnd); |
| + EXPECT_EQ(4, change.acpNewEnd); |
| + |
| + wchar_t buffer[1024]; |
| + ULONG text_buffer_copied; |
| + TS_RUNINFO run_info; |
| + ULONG run_info_buffer_copied; |
| + LONG next_acp; |
| + EXPECT_EQ(S_OK, |
| + text_store_->GetText(0, -1, buffer, 1024, &text_buffer_copied, |
| + &run_info, 1, &run_info_buffer_copied, |
| + &next_acp)); |
| + EXPECT_EQ(5, text_buffer_copied); |
| + EXPECT_EQ(L"axyzc", string16(buffer, buffer + text_buffer_copied)); |
| + EXPECT_EQ(1, run_info_buffer_copied); |
| + EXPECT_EQ(TS_RT_PLAIN, run_info.type); |
| + EXPECT_EQ(5, run_info.uCount); |
| + EXPECT_EQ(5, next_acp); |
| + |
| + composition_undelines()->clear(); |
| + CompositionUnderline underline; |
| + underline.start_offset = 0; |
| + underline.end_offset = 5; |
| + underline.color = SK_ColorBLACK; |
| + underline.thick = false; |
| + composition_undelines()->push_back(underline); |
| + *edit_flag() = true; |
| + *committed_size() = 0; |
| + return S_OK; |
| + } |
| + |
| + void SetCompositionText1(const ui::CompositionText& composition) { |
| + EXPECT_EQ(L"axyzc", composition.text); |
| + EXPECT_EQ(1, composition.selection.start()); |
| + EXPECT_EQ(4, composition.selection.end()); |
| + ASSERT_EQ(1, composition.underlines.size()); |
| + EXPECT_EQ(SK_ColorBLACK, composition.underlines[0].color); |
| + EXPECT_EQ(0, composition.underlines[0].start_offset); |
| + EXPECT_EQ(5, composition.underlines[0].end_offset); |
| + EXPECT_FALSE(composition.underlines[0].thick); |
| + } |
| + |
| + HRESULT LockGranted2(DWORD flags) { |
| + TS_TEXTCHANGE change; |
| + EXPECT_EQ(S_OK, |
| + text_store_->SetText(0, 3, 4, L"ZCP", 3, &change)); |
| + EXPECT_EQ(3, change.acpStart); |
| + EXPECT_EQ(4, change.acpOldEnd); |
| + EXPECT_EQ(6, change.acpNewEnd); |
| + |
| + wchar_t buffer[1024]; |
| + ULONG text_buffer_copied; |
| + TS_RUNINFO run_info; |
| + ULONG run_info_buffer_copied; |
| + LONG next_acp; |
| + EXPECT_EQ(S_OK, |
| + text_store_->GetText(0, -1, buffer, 1024, &text_buffer_copied, |
| + &run_info, 1, &run_info_buffer_copied, |
| + &next_acp)); |
| + EXPECT_EQ(7, text_buffer_copied); |
| + EXPECT_EQ(L"axyZCPc", string16(buffer, buffer + text_buffer_copied)); |
| + EXPECT_EQ(1, run_info_buffer_copied); |
| + EXPECT_EQ(TS_RT_PLAIN, run_info.type); |
| + EXPECT_EQ(7, run_info.uCount); |
| + EXPECT_EQ(7, next_acp); |
| + |
| + composition_undelines()->clear(); |
| + CompositionUnderline underline; |
| + underline.start_offset = 3; |
| + underline.end_offset = 5; |
| + underline.color = SK_ColorBLACK; |
| + underline.thick = true; |
| + composition_undelines()->push_back(underline); |
| + underline.start_offset = 5; |
| + underline.end_offset = 7; |
| + underline.color = SK_ColorBLACK; |
| + underline.thick = false; |
| + composition_undelines()->push_back(underline); |
| + |
| + *edit_flag() = true; |
| + *committed_size() = 3; |
| + |
| + return S_OK; |
| + } |
| + |
| + void InsertText2(const string16& text) { |
| + EXPECT_EQ(L"axy", text); |
| + } |
| + |
| + void SetCompositionText2(const ui::CompositionText& composition) { |
| + EXPECT_EQ(L"ZCPc", composition.text); |
| + EXPECT_EQ(0, composition.selection.start()); |
| + EXPECT_EQ(3, composition.selection.end()); |
| + ASSERT_EQ(2, composition.underlines.size()); |
| + EXPECT_EQ(SK_ColorBLACK, composition.underlines[0].color); |
| + EXPECT_EQ(0, composition.underlines[0].start_offset); |
| + EXPECT_EQ(2, composition.underlines[0].end_offset); |
| + EXPECT_TRUE(composition.underlines[0].thick); |
| + EXPECT_EQ(SK_ColorBLACK, composition.underlines[1].color); |
| + EXPECT_EQ(2, composition.underlines[1].start_offset); |
| + EXPECT_EQ(4, composition.underlines[1].end_offset); |
| + EXPECT_FALSE(composition.underlines[1].thick); |
| + } |
| + |
| + HRESULT LockGranted3(DWORD flags) { |
| + wchar_t buffer[1024]; |
| + ULONG text_buffer_copied; |
| + TS_RUNINFO run_info; |
| + ULONG run_info_buffer_copied; |
| + LONG next_acp; |
| + EXPECT_EQ(S_OK, |
| + text_store_->GetText(0, -1, buffer, 1024, &text_buffer_copied, |
| + &run_info, 1, &run_info_buffer_copied, |
| + &next_acp)); |
| + EXPECT_EQ(7, text_buffer_copied); |
| + EXPECT_EQ(L"axyZCPc", string16(buffer, buffer + text_buffer_copied)); |
| + EXPECT_EQ(1, run_info_buffer_copied); |
| + EXPECT_EQ(TS_RT_PLAIN, run_info.type); |
| + EXPECT_EQ(7, run_info.uCount); |
| + EXPECT_EQ(7, next_acp); |
| + |
| + composition_undelines()->clear(); |
| + *edit_flag() = true; |
| + *committed_size() = 7; |
| + |
| + return S_OK; |
| + } |
| + |
| + void InsertText3(const string16& text) { |
| + EXPECT_EQ(L"ZCPc", text); |
| + } |
| + |
| + void SetCompositionText3(const ui::CompositionText& composition) { |
| + EXPECT_EQ(L"", composition.text); |
| + EXPECT_EQ(0, composition.selection.start()); |
| + EXPECT_EQ(0, composition.selection.end()); |
| + EXPECT_EQ(0, composition.underlines.size()); |
| + } |
| +}; |
| + |
| +TEST_F(TsfTextStoreTest, SenarioTest) { |
| + SenarioTestCallback callback(text_store_); |
| + EXPECT_CALL(text_input_client_, SetCompositionText(_)) |
| + .WillOnce(Invoke(&callback, &SenarioTestCallback::SetCompositionText1)) |
| + .WillOnce(Invoke(&callback, &SenarioTestCallback::SetCompositionText2)) |
| + .WillOnce(Invoke(&callback, &SenarioTestCallback::SetCompositionText3)); |
| + |
| + EXPECT_CALL(text_input_client_, InsertText(_)) |
| + .WillOnce(Invoke(&callback, &SenarioTestCallback::InsertText2)) |
| + .WillOnce(Invoke(&callback, &SenarioTestCallback::InsertText3)); |
| + |
| + EXPECT_CALL(*sink_, OnLockGranted(_)) |
| + .WillOnce(Invoke(&callback, &SenarioTestCallback::LockGranted1)) |
| + .WillOnce(Invoke(&callback, &SenarioTestCallback::LockGranted2)) |
| + .WillOnce(Invoke(&callback, &SenarioTestCallback::LockGranted3)); |
| + |
| + // OnSelectionChange will be called once in LockGranted3(). |
| + EXPECT_CALL(*sink_, OnSelectionChange()) |
| + .WillOnce(Return(S_OK)); |
| + |
| + // OnLayoutChange will be called once in LockGranted3(). |
| + EXPECT_CALL(*sink_, OnLayoutChange(_, _)) |
| + .WillOnce(Return(S_OK)); |
| + |
| + // OnTextChange will be called once in LockGranted3(). |
| + EXPECT_CALL(*sink_, OnTextChange(_, _)) |
| + .WillOnce(Return(S_OK)); |
| + |
| + HRESULT result; |
| + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); |
| + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); |
| + EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); |
| +} |
| + |
| +} // namespace ui |