OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "ui/base/win/tsf_text_store.h" | |
6 | |
7 #include "base/win/scoped_com_initializer.h" | |
8 #include "testing/gtest/include/gtest/gtest.h" | |
9 #include "testing/gmock/include/gmock/gmock.h" | |
10 #include "ui/base/ime/text_input_client.h" | |
11 #include "ui/gfx/rect.h" | |
12 | |
13 using testing::_; | |
14 using testing::Invoke; | |
15 using testing::Return; | |
16 | |
17 namespace ui { | |
18 | |
19 namespace { | |
20 class MockTextInputClient : public TextInputClient { | |
21 public: | |
22 ~MockTextInputClient() {} | |
23 MOCK_METHOD1(SetCompositionText, void(const ui::CompositionText&)); | |
24 MOCK_METHOD0(ConfirmCompositionText, void()); | |
25 MOCK_METHOD0(ClearCompositionText, void()); | |
26 MOCK_METHOD1(InsertText, void(const string16&)); | |
27 MOCK_METHOD2(InsertChar, void(char16, int)); | |
28 MOCK_CONST_METHOD0(GetTextInputType, ui::TextInputType()); | |
29 MOCK_CONST_METHOD0(CanComposeInline, bool()); | |
30 MOCK_METHOD0(GetCaretBounds, gfx::Rect()); | |
31 MOCK_METHOD2(GetCompositionCharacterBounds, bool(uint32, gfx::Rect*)); | |
32 MOCK_METHOD0(HasCompositionText, bool()); | |
33 MOCK_METHOD1(GetTextRange, bool(ui::Range*)); | |
34 MOCK_METHOD1(GetCompositionTextRange, bool(ui::Range*)); | |
35 MOCK_METHOD1(GetSelectionRange, bool(ui::Range*)); | |
36 MOCK_METHOD1(SetSelectionRange, bool(const ui::Range&)); | |
37 MOCK_METHOD1(DeleteRange, bool(const ui::Range&)); | |
38 MOCK_METHOD2(GetTextFromRange, bool(const ui::Range&, string16*)); | |
39 MOCK_METHOD0(OnInputMethodChanged, void()); | |
40 MOCK_METHOD1(ChangeTextDirectionAndLayoutAlignment, | |
41 bool(base::i18n::TextDirection)); | |
42 }; | |
43 | |
44 class MockStoreACPSink : public ITextStoreACPSink { | |
45 public: | |
46 MockStoreACPSink() : ref_count_(0) { | |
47 } | |
48 | |
49 // IUnknown | |
50 virtual ULONG STDMETHODCALLTYPE AddRef() OVERRIDE { | |
51 return InterlockedIncrement(&ref_count_); | |
52 } | |
53 virtual ULONG STDMETHODCALLTYPE Release() OVERRIDE { | |
54 const LONG count = InterlockedDecrement(&ref_count_); | |
55 if (!count) { | |
56 delete this; | |
57 return 0; | |
58 } | |
59 return static_cast<ULONG>(count); | |
60 } | |
61 virtual HRESULT STDMETHODCALLTYPE QueryInterface( | |
62 REFIID iid, void** report) OVERRIDE { | |
63 if (iid == IID_IUnknown || iid == IID_ITextStoreACPSink) { | |
64 *report = static_cast<ITextStoreACPSink*>(this); | |
65 } else { | |
66 *report = NULL; | |
67 return E_NOINTERFACE; | |
68 } | |
69 AddRef(); | |
70 return S_OK; | |
71 } | |
72 | |
73 // ITextStoreACPSink | |
74 MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, OnTextChange, | |
75 HRESULT(DWORD, const TS_TEXTCHANGE*)); | |
76 MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, OnSelectionChange, | |
77 HRESULT()); | |
78 MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, OnLayoutChange, | |
79 HRESULT(TsLayoutCode, TsViewCookie)); | |
80 MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, OnStatusChange, | |
81 HRESULT(DWORD)); | |
82 MOCK_METHOD4_WITH_CALLTYPE(STDMETHODCALLTYPE, OnAttrsChange, | |
83 HRESULT(LONG, LONG, ULONG, const TS_ATTRID*)); | |
84 MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, OnLockGranted, | |
85 HRESULT(DWORD)); | |
86 MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, OnStartEditTransaction, | |
87 HRESULT()); | |
88 MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, OnEndEditTransaction, | |
89 HRESULT()); | |
90 | |
91 private: | |
92 ~MockStoreACPSink() { | |
93 } | |
94 volatile LONG ref_count_; | |
95 }; | |
96 | |
97 } // namespace | |
98 | |
99 class TsfTextStoreTest : public testing::Test { | |
100 protected: | |
101 virtual void SetUp() OVERRIDE { | |
102 text_store_ = new TsfTextStore(); | |
103 text_store_->AddRef(); | |
104 sink_ = new MockStoreACPSink(); | |
105 sink_->AddRef(); | |
106 EXPECT_EQ(S_OK, | |
107 text_store_->AdviseSink(IID_ITextStoreACPSink, | |
Yohei Yukawa
2012/08/23 10:16:44
Indent?
horo
2012/08/23 10:36:18
Done.
| |
108 sink_, TS_AS_ALL_SINKS)); | |
109 text_store_->SetFocusedTextInputClient(0, &text_input_client_); | |
110 } | |
111 | |
112 virtual void TearDown() OVERRIDE { | |
113 EXPECT_EQ(S_OK, text_store_->UnadviseSink(sink_)); | |
114 sink_->Release(); | |
115 text_store_->Release(); | |
116 } | |
117 | |
118 base::win::ScopedCOMInitializer com_initializer_; | |
119 MockTextInputClient text_input_client_; | |
120 TsfTextStore* text_store_; | |
121 MockStoreACPSink* sink_; | |
122 }; | |
123 | |
124 class TsfTextStoreTestCallback { | |
125 public: | |
126 explicit TsfTextStoreTestCallback(TsfTextStore* text_store) | |
127 : text_store_(text_store) { | |
128 CHECK(text_store_); | |
129 } | |
130 virtual ~TsfTextStoreTestCallback() {} | |
131 | |
132 protected: | |
133 // Accessros to the internal state of TsfTextStore. | |
134 bool* edit_flag() { return &text_store_->edit_flag_; } | |
135 string16* string_buffer() { return &text_store_->string_buffer_; } | |
136 size_t* committed_size() { return &text_store_->committed_size_; } | |
137 Range* selection() { return &text_store_->selection_; } | |
138 CompositionUnderlines* composition_undelines() { | |
139 return &text_store_->composition_undelines_; | |
140 } | |
141 | |
142 TsfTextStore* text_store_; | |
143 }; | |
144 | |
145 class SelectionTestCallback : public TsfTextStoreTestCallback { | |
146 public: | |
147 explicit SelectionTestCallback(TsfTextStore* text_store) | |
148 : TsfTextStoreTestCallback(text_store) { | |
149 } | |
150 | |
151 HRESULT ReadLockGranted(DWORD flags) { | |
152 *string_buffer() = L""; | |
153 *committed_size() = 0; | |
154 selection()->set_start(0); | |
155 selection()->set_end(0); | |
156 | |
157 GetSelectionTest(0, 0); | |
158 SetSelectionTest(0, 0, TF_E_NOLOCK); | |
159 | |
160 *string_buffer() = L"012345"; | |
161 *committed_size() = 0; | |
162 selection()->set_start(0); | |
163 selection()->set_end(3); | |
164 | |
165 GetSelectionTest(0, 3); | |
166 SetSelectionTest(0, 0, TF_E_NOLOCK); | |
167 | |
168 return S_OK; | |
169 } | |
170 | |
171 HRESULT ReadWriteLockGranted(DWORD flags) { | |
172 *string_buffer() = L""; | |
173 *committed_size() = 0; | |
174 selection()->set_start(0); | |
175 selection()->set_end(0); | |
176 | |
177 SetSelectionTest(0, 0, S_OK); | |
178 GetSelectionTest(0, 0); | |
179 SetSelectionTest(0, 1, TF_E_INVALIDPOS); | |
180 GetSelectionTest(0, 0); | |
181 SetSelectionTest(1, 0, TF_E_INVALIDPOS); | |
182 GetSelectionTest(0, 0); | |
183 SetSelectionTest(1, 1, TF_E_INVALIDPOS); | |
184 GetSelectionTest(0, 0); | |
185 | |
186 *string_buffer() = L"012345"; | |
187 *committed_size() = 0; | |
188 selection()->set_start(0); | |
189 selection()->set_end(0); | |
190 | |
191 SetSelectionTest(0, 0, S_OK); | |
192 GetSelectionTest(0, 0); | |
193 SetSelectionTest(0, 3, S_OK); | |
194 GetSelectionTest(0, 3); | |
195 SetSelectionTest(0, 6, S_OK); | |
196 GetSelectionTest(0, 6); | |
197 SetSelectionTest(0, 7, TF_E_INVALIDPOS); | |
198 GetSelectionTest(0, 6); | |
199 | |
200 SetSelectionTest(3, 0, TF_E_INVALIDPOS); | |
201 GetSelectionTest(0, 6); | |
202 SetSelectionTest(3, 3, S_OK); | |
203 GetSelectionTest(3, 3); | |
204 SetSelectionTest(3, 6, S_OK); | |
205 GetSelectionTest(3, 6); | |
206 SetSelectionTest(3, 7, TF_E_INVALIDPOS); | |
207 GetSelectionTest(3, 6); | |
208 | |
209 SetSelectionTest(6, 0, TF_E_INVALIDPOS); | |
210 GetSelectionTest(3, 6); | |
211 SetSelectionTest(6, 3, TF_E_INVALIDPOS); | |
212 GetSelectionTest(3, 6); | |
213 SetSelectionTest(6, 6, S_OK); | |
214 GetSelectionTest(6, 6); | |
215 SetSelectionTest(6, 7, TF_E_INVALIDPOS); | |
216 GetSelectionTest(6, 6); | |
217 | |
218 return S_OK; | |
219 } | |
220 | |
221 private: | |
222 void SetSelectionTest(LONG acp_start, LONG acp_end, HRESULT expected_result) { | |
223 TS_SELECTION_ACP selection; | |
224 selection.acpStart = acp_start; | |
225 selection.acpEnd = acp_end; | |
226 selection.style.ase = TS_AE_NONE; | |
227 selection.style.fInterimChar = 0; | |
228 EXPECT_EQ(expected_result, text_store_->SetSelection(1, &selection)); | |
229 } | |
230 | |
231 void GetSelectionTest(LONG expected_acp_start, LONG expected_acp_end) { | |
232 TS_SELECTION_ACP selection; | |
233 ULONG fetched; | |
234 EXPECT_EQ(S_OK, text_store_->GetSelection(0, 1, &selection, &fetched)); | |
235 EXPECT_EQ(1, fetched); | |
236 EXPECT_EQ(expected_acp_start, selection.acpStart); | |
237 EXPECT_EQ(expected_acp_end, selection.acpEnd); | |
238 } | |
239 }; | |
240 | |
241 TEST_F(TsfTextStoreTest, GetStatus) { | |
242 TS_STATUS status; | |
243 EXPECT_EQ(S_OK, text_store_->GetStatus(&status)); | |
244 EXPECT_EQ(0, status.dwDynamicFlags); | |
245 EXPECT_EQ(TS_SS_NOHIDDENTEXT, status.dwStaticFlags); | |
246 } | |
247 | |
248 TEST_F(TsfTextStoreTest, SetGetSelectionTest) { | |
249 SelectionTestCallback callback(text_store_); | |
250 EXPECT_CALL(*sink_, OnLockGranted(_)) | |
251 .WillOnce(Invoke(&callback, &SelectionTestCallback::ReadLockGranted)) | |
252 .WillOnce(Invoke(&callback, | |
253 &SelectionTestCallback::ReadWriteLockGranted)); | |
254 | |
255 TS_SELECTION_ACP selection_buffer; | |
256 ULONG fetched_count; | |
257 EXPECT_EQ(TS_E_NOLOCK, | |
258 text_store_->GetSelection(0, 1, &selection_buffer, | |
259 &fetched_count)); | |
260 | |
261 HRESULT result; | |
262 EXPECT_EQ(S_OK, | |
263 text_store_->RequestLock(TS_LF_READ, &result)); | |
264 EXPECT_EQ(S_OK, | |
265 text_store_->RequestLock(TS_LF_READWRITE, &result)); | |
266 } | |
267 | |
268 class SenarioTestCallback : public TsfTextStoreTestCallback { | |
269 public: | |
270 explicit SenarioTestCallback(TsfTextStore* text_store) | |
271 : TsfTextStoreTestCallback(text_store) { | |
272 } | |
273 | |
274 HRESULT LockGranted1(DWORD flags) { | |
275 TS_SELECTION_ACP selection; | |
276 selection.acpStart = 0; | |
277 selection.acpEnd = 0; | |
278 selection.style.ase = TS_AE_NONE; | |
279 selection.style.fInterimChar = 0; | |
280 EXPECT_EQ(S_OK, | |
281 text_store_->SetSelection(1, &selection)); | |
282 TS_TEXTCHANGE change; | |
283 EXPECT_EQ(S_OK, | |
284 text_store_->SetText(0, 0, 0, L"abc", 3, &change)); | |
285 EXPECT_EQ(0, change.acpStart); | |
286 EXPECT_EQ(0, change.acpOldEnd); | |
287 EXPECT_EQ(3, change.acpNewEnd); | |
288 | |
289 EXPECT_EQ(S_OK, | |
290 text_store_->SetText(0, 1, 2, L"xyz", 3, &change)); | |
291 EXPECT_EQ(1, change.acpStart); | |
292 EXPECT_EQ(2, change.acpOldEnd); | |
293 EXPECT_EQ(4, change.acpNewEnd); | |
294 | |
295 wchar_t buffer[1024]; | |
296 ULONG text_buffer_copied; | |
297 TS_RUNINFO run_info; | |
298 ULONG run_info_buffer_copied; | |
299 LONG next_acp; | |
300 EXPECT_EQ(S_OK, | |
301 text_store_->GetText(0, -1, buffer, 1024, &text_buffer_copied, | |
302 &run_info, 1, &run_info_buffer_copied, | |
303 &next_acp)); | |
304 EXPECT_EQ(5, text_buffer_copied); | |
305 EXPECT_EQ(L"axyzc", string16(buffer, buffer + text_buffer_copied)); | |
306 EXPECT_EQ(1, run_info_buffer_copied); | |
307 EXPECT_EQ(TS_RT_PLAIN, run_info.type); | |
308 EXPECT_EQ(5, run_info.uCount); | |
309 EXPECT_EQ(5, next_acp); | |
310 | |
311 composition_undelines()->clear(); | |
312 CompositionUnderline underline; | |
313 underline.start_offset = 0; | |
314 underline.end_offset = 5; | |
315 underline.color = SK_ColorBLACK; | |
316 underline.thick = false; | |
317 composition_undelines()->push_back(underline); | |
318 *edit_flag() = true; | |
319 *committed_size() = 0; | |
320 return S_OK; | |
321 } | |
322 | |
323 void SetCompositionText1(const ui::CompositionText& composition) { | |
324 EXPECT_EQ(L"axyzc", composition.text); | |
325 EXPECT_EQ(1, composition.selection.start()); | |
326 EXPECT_EQ(4, composition.selection.end()); | |
327 ASSERT_EQ(1, composition.underlines.size()); | |
328 EXPECT_EQ(SK_ColorBLACK, composition.underlines[0].color); | |
329 EXPECT_EQ(0, composition.underlines[0].start_offset); | |
330 EXPECT_EQ(5, composition.underlines[0].end_offset); | |
331 EXPECT_FALSE(composition.underlines[0].thick); | |
332 } | |
333 | |
334 HRESULT LockGranted2(DWORD flags) { | |
335 TS_TEXTCHANGE change; | |
336 EXPECT_EQ(S_OK, | |
337 text_store_->SetText(0, 3, 4, L"ZCP", 3, &change)); | |
338 EXPECT_EQ(3, change.acpStart); | |
339 EXPECT_EQ(4, change.acpOldEnd); | |
340 EXPECT_EQ(6, change.acpNewEnd); | |
341 | |
342 wchar_t buffer[1024]; | |
343 ULONG text_buffer_copied; | |
344 TS_RUNINFO run_info; | |
345 ULONG run_info_buffer_copied; | |
346 LONG next_acp; | |
347 EXPECT_EQ(S_OK, | |
348 text_store_->GetText(0, -1, buffer, 1024, &text_buffer_copied, | |
349 &run_info, 1, &run_info_buffer_copied, | |
350 &next_acp)); | |
351 EXPECT_EQ(7, text_buffer_copied); | |
352 EXPECT_EQ(L"axyZCPc", string16(buffer, buffer + text_buffer_copied)); | |
353 EXPECT_EQ(1, run_info_buffer_copied); | |
354 EXPECT_EQ(TS_RT_PLAIN, run_info.type); | |
355 EXPECT_EQ(7, run_info.uCount); | |
356 EXPECT_EQ(7, next_acp); | |
357 | |
358 composition_undelines()->clear(); | |
359 CompositionUnderline underline; | |
360 underline.start_offset = 3; | |
361 underline.end_offset = 5; | |
362 underline.color = SK_ColorBLACK; | |
363 underline.thick = true; | |
364 composition_undelines()->push_back(underline); | |
365 underline.start_offset = 5; | |
366 underline.end_offset = 7; | |
367 underline.color = SK_ColorBLACK; | |
368 underline.thick = false; | |
369 composition_undelines()->push_back(underline); | |
370 | |
371 *edit_flag() = true; | |
372 *committed_size() = 3; | |
373 | |
374 return S_OK; | |
375 } | |
376 | |
377 void InsertText2(const string16& text) { | |
378 EXPECT_EQ(L"axy", text); | |
379 } | |
380 | |
381 void SetCompositionText2(const ui::CompositionText& composition) { | |
382 EXPECT_EQ(L"ZCPc", composition.text); | |
383 EXPECT_EQ(0, composition.selection.start()); | |
384 EXPECT_EQ(3, composition.selection.end()); | |
385 ASSERT_EQ(2, composition.underlines.size()); | |
386 EXPECT_EQ(SK_ColorBLACK, composition.underlines[0].color); | |
387 EXPECT_EQ(0, composition.underlines[0].start_offset); | |
388 EXPECT_EQ(2, composition.underlines[0].end_offset); | |
389 EXPECT_TRUE(composition.underlines[0].thick); | |
390 EXPECT_EQ(SK_ColorBLACK, composition.underlines[1].color); | |
391 EXPECT_EQ(2, composition.underlines[1].start_offset); | |
392 EXPECT_EQ(4, composition.underlines[1].end_offset); | |
393 EXPECT_FALSE(composition.underlines[1].thick); | |
394 } | |
395 | |
396 HRESULT LockGranted3(DWORD flags) { | |
397 wchar_t buffer[1024]; | |
398 ULONG text_buffer_copied; | |
399 TS_RUNINFO run_info; | |
400 ULONG run_info_buffer_copied; | |
401 LONG next_acp; | |
402 EXPECT_EQ(S_OK, | |
403 text_store_->GetText(0, -1, buffer, 1024, &text_buffer_copied, | |
404 &run_info, 1, &run_info_buffer_copied, | |
405 &next_acp)); | |
406 EXPECT_EQ(7, text_buffer_copied); | |
407 EXPECT_EQ(L"axyZCPc", string16(buffer, buffer + text_buffer_copied)); | |
408 EXPECT_EQ(1, run_info_buffer_copied); | |
409 EXPECT_EQ(TS_RT_PLAIN, run_info.type); | |
410 EXPECT_EQ(7, run_info.uCount); | |
411 EXPECT_EQ(7, next_acp); | |
412 | |
413 composition_undelines()->clear(); | |
414 *edit_flag() = true; | |
415 *committed_size() = 7; | |
416 | |
417 return S_OK; | |
418 } | |
419 | |
420 void InsertText3(const string16& text) { | |
421 EXPECT_EQ(L"ZCPc", text); | |
422 } | |
423 | |
424 void SetCompositionText3(const ui::CompositionText& composition) { | |
425 EXPECT_EQ(L"", composition.text); | |
426 EXPECT_EQ(0, composition.selection.start()); | |
427 EXPECT_EQ(0, composition.selection.end()); | |
428 EXPECT_EQ(0, composition.underlines.size()); | |
429 } | |
430 }; | |
431 | |
432 TEST_F(TsfTextStoreTest, SenarioTest) { | |
433 SenarioTestCallback callback(text_store_); | |
434 EXPECT_CALL(text_input_client_, SetCompositionText(_)) | |
435 .WillOnce(Invoke(&callback, &SenarioTestCallback::SetCompositionText1)) | |
436 .WillOnce(Invoke(&callback, &SenarioTestCallback::SetCompositionText2)) | |
437 .WillOnce(Invoke(&callback, &SenarioTestCallback::SetCompositionText3)); | |
438 | |
439 EXPECT_CALL(text_input_client_, InsertText(_)) | |
440 .WillOnce(Invoke(&callback, &SenarioTestCallback::InsertText2)) | |
441 .WillOnce(Invoke(&callback, &SenarioTestCallback::InsertText3)); | |
442 | |
443 EXPECT_CALL(*sink_, OnLockGranted(_)) | |
444 .WillOnce(Invoke(&callback, &SenarioTestCallback::LockGranted1)) | |
445 .WillOnce(Invoke(&callback, &SenarioTestCallback::LockGranted2)) | |
446 .WillOnce(Invoke(&callback, &SenarioTestCallback::LockGranted3)); | |
447 | |
448 // OnSelectionChange will be called once in LockGranted3(). | |
449 EXPECT_CALL(*sink_, OnSelectionChange()) | |
450 .WillOnce(Return(S_OK)); | |
451 | |
452 // OnLayoutChange will be called once in LockGranted3(). | |
453 EXPECT_CALL(*sink_, OnLayoutChange(_, _)) | |
454 .WillOnce(Return(S_OK)); | |
455 | |
456 // OnTextChange will be called once in LockGranted3(). | |
457 EXPECT_CALL(*sink_, OnTextChange(_, _)) | |
458 .WillOnce(Return(S_OK)); | |
459 | |
460 HRESULT result; | |
461 EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); | |
462 EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); | |
463 EXPECT_EQ(S_OK, text_store_->RequestLock(TS_LF_READWRITE, &result)); | |
464 } | |
465 | |
466 } // namespace ui | |
OLD | NEW |