xref: /aosp_15_r20/external/pdfium/xfa/fde/cfde_texteditengine_unittest.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2017 The PDFium Authors
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 "xfa/fde/cfde_texteditengine.h"
6 
7 #include "core/fxcrt/fx_codepage.h"
8 #include "core/fxcrt/fx_extension.h"
9 #include "core/fxge/text_char_pos.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "testing/xfa_test_environment.h"
12 #include "xfa/fgas/font/cfgas_gefont.h"
13 
14 class CFDE_TextEditEngineTest : public testing::Test {
15  public:
16   class Delegate final : public CFDE_TextEditEngine::Delegate {
17    public:
Reset()18     void Reset() {
19       text_is_full = false;
20       fail_validation = false;
21     }
22 
NotifyTextFull()23     void NotifyTextFull() override { text_is_full = true; }
24 
OnCaretChanged()25     void OnCaretChanged() override {}
OnTextWillChange(CFDE_TextEditEngine::TextChange * change)26     void OnTextWillChange(CFDE_TextEditEngine::TextChange* change) override {}
OnTextChanged()27     void OnTextChanged() override {}
OnSelChanged()28     void OnSelChanged() override {}
OnValidate(const WideString & wsText)29     bool OnValidate(const WideString& wsText) override {
30       return !fail_validation;
31     }
SetScrollOffset(float fScrollOffset)32     void SetScrollOffset(float fScrollOffset) override {}
33 
34     bool fail_validation = false;
35     bool text_is_full = false;
36   };
37 
38   CFDE_TextEditEngineTest() = default;
39   ~CFDE_TextEditEngineTest() override = default;
40 
SetUp()41   void SetUp() override {
42     const wchar_t kFontFamily[] = L"Arimo Bold";
43     font_ = CFGAS_GEFont::LoadFont(kFontFamily, 0, FX_CodePage::kDefANSI);
44     ASSERT_TRUE(font_);
45 
46     engine_ = std::make_unique<CFDE_TextEditEngine>();
47     engine_->SetFont(font_);
48     engine_->SetFontSize(12.0f);
49   }
50 
TearDown()51   void TearDown() override {
52     engine_.reset();
53     font_.Reset();
54   }
55 
engine() const56   CFDE_TextEditEngine* engine() const { return engine_.get(); }
57 
58  private:
59   RetainPtr<CFGAS_GEFont> font_;
60   std::unique_ptr<CFDE_TextEditEngine> engine_;
61 };
62 
TEST_F(CFDE_TextEditEngineTest,Insert)63 TEST_F(CFDE_TextEditEngineTest, Insert) {
64   EXPECT_STREQ(L"", engine()->GetText().c_str());
65 
66   engine()->Insert(0, L"");
67   EXPECT_STREQ(L"", engine()->GetText().c_str());
68   EXPECT_EQ(0U, engine()->GetLength());
69 
70   engine()->Insert(0, L"Hello");
71   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
72   EXPECT_EQ(5U, engine()->GetLength());
73 
74   engine()->Insert(5, L" World");
75   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
76   EXPECT_EQ(11U, engine()->GetLength());
77 
78   engine()->Insert(5, L" New");
79   EXPECT_STREQ(L"Hello New World", engine()->GetText().c_str());
80 
81   engine()->Insert(100, L" Cat");
82   EXPECT_STREQ(L"Hello New World Cat", engine()->GetText().c_str());
83 
84   engine()->Clear();
85 
86   engine()->SetHasCharacterLimit(true);
87   engine()->SetCharacterLimit(5);
88   engine()->Insert(0, L"Hello");
89 
90   // No delegate
91   engine()->Insert(5, L" World");
92   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
93 
94   engine()->SetCharacterLimit(8);
95   engine()->Insert(5, L" World");
96   EXPECT_STREQ(L"Hello Wo", engine()->GetText().c_str());
97 
98   engine()->Clear();
99 
100   // With Delegate
101   auto delegate = std::make_unique<CFDE_TextEditEngineTest::Delegate>();
102   engine()->SetDelegate(delegate.get());
103 
104   engine()->SetCharacterLimit(5);
105   engine()->Insert(0, L"Hello");
106 
107   // Insert when full.
108   engine()->Insert(5, L" World");
109   EXPECT_TRUE(delegate->text_is_full);
110   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
111   delegate->Reset();
112 
113   engine()->SetCharacterLimit(8);
114   engine()->Insert(5, L" World");
115   EXPECT_TRUE(delegate->text_is_full);
116   EXPECT_STREQ(L"Hello Wo", engine()->GetText().c_str());
117   delegate->Reset();
118   engine()->SetHasCharacterLimit(false);
119 
120   engine()->Clear();
121   engine()->Insert(0, L"Hello");
122 
123   // Insert Invalid text
124   delegate->fail_validation = true;
125   engine()->EnableValidation(true);
126   engine()->Insert(5, L" World");
127   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
128 
129   delegate->fail_validation = false;
130   engine()->Insert(5, L" World");
131   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
132   engine()->EnableValidation(false);
133 
134   engine()->Clear();
135 
136   engine()->Insert(0, L"Hello\nWorld");
137   EXPECT_FALSE(delegate->text_is_full);
138   EXPECT_STREQ(L"Hello\nWorld", engine()->GetText().c_str());
139   delegate->Reset();
140   engine()->Clear();
141 
142   // Insert with limited area and over-fill
143   engine()->LimitHorizontalScroll(true);
144   engine()->SetAvailableWidth(52.0f);  // Fits 'Hello Wo'.
145   engine()->Insert(0, L"Hello");
146   EXPECT_FALSE(delegate->text_is_full);
147   engine()->Insert(5, L" World");
148   EXPECT_TRUE(delegate->text_is_full);
149   EXPECT_STREQ(L"Hello Wo", engine()->GetText().c_str());
150   engine()->LimitHorizontalScroll(false);
151 
152   delegate->Reset();
153   engine()->Clear();
154 
155   engine()->SetLineSpace(12.0f);
156   engine()->LimitVerticalScroll(true);
157   // Default is one line of text.
158   engine()->Insert(0, L"Hello");
159   EXPECT_FALSE(delegate->text_is_full);
160   engine()->Insert(5, L" Wo\nrld");
161   EXPECT_TRUE(delegate->text_is_full);
162   EXPECT_STREQ(L"Hello Wo\n", engine()->GetText().c_str());
163   engine()->LimitVerticalScroll(false);
164 
165   engine()->SetDelegate(nullptr);
166 }
167 
TEST_F(CFDE_TextEditEngineTest,InsertToggleLimit)168 TEST_F(CFDE_TextEditEngineTest, InsertToggleLimit) {
169   engine()->SetHasCharacterLimit(true);
170   engine()->Insert(0, L"Hello World");
171   engine()->SetCharacterLimit(5);
172   engine()->Insert(0, L"Not Inserted before ");
173   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
174 
175   engine()->SetHasCharacterLimit(false);
176   engine()->Insert(0, L"Inserted before ");
177   engine()->SetHasCharacterLimit(true);
178   engine()->Insert(0, L"Not Inserted before ");
179   EXPECT_STREQ(L"Inserted before Hello World", engine()->GetText().c_str());
180 }
181 
TEST_F(CFDE_TextEditEngineTest,InsertSkipNotify)182 TEST_F(CFDE_TextEditEngineTest, InsertSkipNotify) {
183   engine()->SetHasCharacterLimit(true);
184   engine()->SetCharacterLimit(8);
185   engine()->Insert(0, L"Hello");
186   engine()->Insert(5, L" World",
187                    CFDE_TextEditEngine::RecordOperation::kSkipNotify);
188   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
189 
190   engine()->Insert(0, L"Not inserted");
191   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
192 
193   engine()->Delete(5, 1);
194   EXPECT_STREQ(L"HelloWorld", engine()->GetText().c_str());
195 
196   engine()->Insert(0, L"****");
197   EXPECT_STREQ(L"*HelloWorld", engine()->GetText().c_str());
198 }
199 
TEST_F(CFDE_TextEditEngineTest,InsertGrowGap)200 TEST_F(CFDE_TextEditEngineTest, InsertGrowGap) {
201   engine()->Insert(0, L"||");
202   for (size_t i = 1; i < 1023; ++i) {
203     engine()->Insert(i, L"a");
204   }
205   WideString result = engine()->GetText();
206   ASSERT_EQ(result.GetLength(), 1024u);
207   EXPECT_EQ(result[0], L'|');
208   EXPECT_EQ(result[1], L'a');
209   EXPECT_EQ(result[2], L'a');
210   // ...
211   EXPECT_EQ(result[1022], L'a');
212   EXPECT_EQ(result[1023], L'|');
213 }
214 
TEST_F(CFDE_TextEditEngineTest,Delete)215 TEST_F(CFDE_TextEditEngineTest, Delete) {
216   EXPECT_STREQ(L"", engine()->Delete(0, 50).c_str());
217   EXPECT_STREQ(L"", engine()->GetText().c_str());
218 
219   engine()->Insert(0, L"Hello World");
220   EXPECT_STREQ(L" World", engine()->Delete(5, 6).c_str());
221   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
222 
223   engine()->Clear();
224   engine()->Insert(0, L"Hello World");
225   EXPECT_STREQ(L" ", engine()->Delete(5, 1).c_str());
226   EXPECT_STREQ(L"HelloWorld", engine()->GetText().c_str());
227 
228   EXPECT_STREQ(L"elloWorld", engine()->Delete(1, 50).c_str());
229   EXPECT_STREQ(L"H", engine()->GetText().c_str());
230 }
231 
TEST_F(CFDE_TextEditEngineTest,Clear)232 TEST_F(CFDE_TextEditEngineTest, Clear) {
233   EXPECT_STREQ(L"", engine()->GetText().c_str());
234 
235   engine()->Clear();
236   EXPECT_STREQ(L"", engine()->GetText().c_str());
237 
238   engine()->Insert(0, L"Hello World");
239   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
240 
241   engine()->Clear();
242   EXPECT_STREQ(L"", engine()->GetText().c_str());
243   EXPECT_EQ(0U, engine()->GetLength());
244 }
245 
TEST_F(CFDE_TextEditEngineTest,GetChar)246 TEST_F(CFDE_TextEditEngineTest, GetChar) {
247   // Out of bounds.
248   EXPECT_EQ(L'\0', engine()->GetChar(0));
249 
250   engine()->Insert(0, L"Hello World");
251   EXPECT_EQ(L'H', engine()->GetChar(0));
252   EXPECT_EQ(L'd', engine()->GetChar(engine()->GetLength() - 1));
253   EXPECT_EQ(L' ', engine()->GetChar(5));
254 
255   engine()->Insert(5, L" A");
256   EXPECT_STREQ(L"Hello A World", engine()->GetText().c_str());
257   EXPECT_EQ(L'W', engine()->GetChar(8));
258 
259   engine()->EnablePasswordMode(true);
260   EXPECT_EQ(L'*', engine()->GetChar(8));
261 
262   engine()->SetAliasChar(L'+');
263   EXPECT_EQ(L'+', engine()->GetChar(8));
264 }
265 
TEST_F(CFDE_TextEditEngineTest,GetWidthOfChar)266 TEST_F(CFDE_TextEditEngineTest, GetWidthOfChar) {
267   // Out of Bounds.
268   EXPECT_EQ(0, engine()->GetWidthOfChar(0));
269 
270   engine()->Insert(0, L"Hello World");
271   EXPECT_EQ(173280, engine()->GetWidthOfChar(0));
272   EXPECT_EQ(133440, engine()->GetWidthOfChar(1));
273 
274   engine()->Insert(0, L"\t");
275   EXPECT_EQ(0, engine()->GetWidthOfChar(0));
276 }
277 
TEST_F(CFDE_TextEditEngineTest,GetDisplayPos)278 TEST_F(CFDE_TextEditEngineTest, GetDisplayPos) {
279   EXPECT_EQ(0U, engine()->GetDisplayPos(FDE_TEXTEDITPIECE()).size());
280 }
281 
TEST_F(CFDE_TextEditEngineTest,Selection)282 TEST_F(CFDE_TextEditEngineTest, Selection) {
283   EXPECT_FALSE(engine()->HasSelection());
284   engine()->SelectAll();
285   EXPECT_FALSE(engine()->HasSelection());
286 
287   engine()->Insert(0, L"Hello World");
288   EXPECT_STREQ(L"", engine()->DeleteSelectedText().c_str());
289 
290   EXPECT_FALSE(engine()->HasSelection());
291   engine()->SelectAll();
292   EXPECT_TRUE(engine()->HasSelection());
293   EXPECT_STREQ(L"Hello World", engine()->GetSelectedText().c_str());
294 
295   engine()->ClearSelection();
296   EXPECT_FALSE(engine()->HasSelection());
297   EXPECT_STREQ(L"", engine()->GetSelectedText().c_str());
298 
299   engine()->SelectAll();
300   size_t start_idx;
301   size_t count;
302   std::tie(start_idx, count) = engine()->GetSelection();
303   EXPECT_EQ(0U, start_idx);
304   EXPECT_EQ(11U, count);
305 
306   // Selection before gap.
307   EXPECT_STREQ(L"Hello World", engine()->GetSelectedText().c_str());
308   EXPECT_TRUE(engine()->HasSelection());
309   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
310 
311   engine()->Insert(5, L" A");
312   EXPECT_FALSE(engine()->HasSelection());
313   EXPECT_STREQ(L"", engine()->GetSelectedText().c_str());
314 
315   // Selection over the gap.
316   engine()->SelectAll();
317   EXPECT_TRUE(engine()->HasSelection());
318   EXPECT_STREQ(L"Hello A World", engine()->GetSelectedText().c_str());
319   engine()->Clear();
320 
321   engine()->Insert(0, L"Hello World");
322   engine()->SelectAll();
323 
324   EXPECT_STREQ(L"Hello World", engine()->DeleteSelectedText().c_str());
325   EXPECT_FALSE(engine()->HasSelection());
326   EXPECT_STREQ(L"", engine()->GetText().c_str());
327 
328   engine()->Insert(0, L"Hello World");
329   engine()->SetSelection(5, 5);
330   EXPECT_STREQ(L" Worl", engine()->DeleteSelectedText().c_str());
331   EXPECT_FALSE(engine()->HasSelection());
332   EXPECT_STREQ(L"Hellod", engine()->GetText().c_str());
333 
334   engine()->Clear();
335   engine()->Insert(0, L"Hello World");
336   engine()->SelectAll();
337   engine()->ReplaceSelectedText(L"Goodbye Everybody");
338   EXPECT_FALSE(engine()->HasSelection());
339   EXPECT_STREQ(L"Goodbye Everybody", engine()->GetText().c_str());
340 
341   engine()->Clear();
342   engine()->Insert(0, L"Hello World");
343   engine()->SetSelection(1, 4);
344   engine()->ReplaceSelectedText(L"i,");
345   EXPECT_FALSE(engine()->HasSelection());
346   EXPECT_STREQ(L"Hi, World", engine()->GetText().c_str());
347 
348   // Selection fully after gap.
349   engine()->Clear();
350   engine()->Insert(0, L"Hello");
351   engine()->Insert(0, L"A ");
352   engine()->SetSelection(3, 6);
353   EXPECT_STREQ(L"ello", engine()->GetSelectedText().c_str());
354 
355   engine()->Clear();
356   engine()->Insert(0, L"Hello World");
357   engine()->ClearSelection();
358   engine()->DeleteSelectedText();
359   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
360 }
361 
TEST_F(CFDE_TextEditEngineTest,UndoRedo)362 TEST_F(CFDE_TextEditEngineTest, UndoRedo) {
363   EXPECT_FALSE(engine()->CanUndo());
364   EXPECT_FALSE(engine()->CanRedo());
365   EXPECT_FALSE(engine()->Undo());
366   EXPECT_FALSE(engine()->Redo());
367 
368   engine()->Insert(0, L"Hello");
369   EXPECT_TRUE(engine()->CanUndo());
370   EXPECT_FALSE(engine()->CanRedo());
371   EXPECT_TRUE(engine()->Undo());
372   EXPECT_STREQ(L"", engine()->GetText().c_str());
373   EXPECT_FALSE(engine()->CanUndo());
374   EXPECT_TRUE(engine()->CanRedo());
375   EXPECT_TRUE(engine()->Redo());
376   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
377   EXPECT_TRUE(engine()->CanUndo());
378   EXPECT_FALSE(engine()->CanRedo());
379 
380   engine()->Clear();
381   EXPECT_FALSE(engine()->CanUndo());
382   EXPECT_FALSE(engine()->CanRedo());
383 
384   engine()->Insert(0, L"Hello World");
385   engine()->SelectAll();
386   engine()->DeleteSelectedText();
387   EXPECT_STREQ(L"", engine()->GetText().c_str());
388   EXPECT_TRUE(engine()->CanUndo());
389   EXPECT_TRUE(engine()->Undo());
390   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
391   EXPECT_TRUE(engine()->CanRedo());
392   EXPECT_TRUE(engine()->Redo());
393   EXPECT_STREQ(L"", engine()->GetText().c_str());
394   EXPECT_TRUE(engine()->CanUndo());
395   EXPECT_FALSE(engine()->CanRedo());
396 
397   engine()->Insert(0, L"Hello World");
398   engine()->SelectAll();
399   engine()->ReplaceSelectedText(L"Goodbye Friend");
400   EXPECT_STREQ(L"Goodbye Friend", engine()->GetText().c_str());
401   EXPECT_TRUE(engine()->CanUndo());
402   EXPECT_TRUE(engine()->Undo());
403   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
404   EXPECT_TRUE(engine()->CanRedo());
405   EXPECT_TRUE(engine()->Redo());
406   EXPECT_STREQ(L"Goodbye Friend", engine()->GetText().c_str());
407 
408   engine()->Clear();
409   engine()->SetMaxEditOperationsForTesting(3);
410   engine()->Insert(0, L"First ");
411   engine()->Insert(engine()->GetLength(), L"Second ");
412   engine()->Insert(engine()->GetLength(), L"Third");
413 
414   EXPECT_TRUE(engine()->CanUndo());
415   EXPECT_TRUE(engine()->Undo());
416   EXPECT_STREQ(L"First Second ", engine()->GetText().c_str());
417   EXPECT_TRUE(engine()->CanUndo());
418   EXPECT_TRUE(engine()->Undo());
419   EXPECT_FALSE(
420       engine()->CanUndo());  // Can't undo First; undo buffer too small.
421   EXPECT_STREQ(L"First ", engine()->GetText().c_str());
422 
423   EXPECT_TRUE(engine()->CanRedo());
424   EXPECT_TRUE(engine()->Redo());
425   EXPECT_TRUE(engine()->CanRedo());
426   EXPECT_TRUE(engine()->Redo());
427   EXPECT_FALSE(engine()->CanRedo());
428   EXPECT_STREQ(L"First Second Third", engine()->GetText().c_str());
429 
430   engine()->Clear();
431 
432   engine()->SetMaxEditOperationsForTesting(4);
433 
434   // Go beyond the max operations limit.
435   engine()->Insert(0, L"H");
436   engine()->Insert(1, L"e");
437   engine()->Insert(2, L"l");
438   engine()->Insert(3, L"l");
439   engine()->Insert(4, L"o");
440   engine()->Insert(5, L" World");
441   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
442 
443   // Do A, undo. Do B, undo. Redo should cause B.
444   engine()->Delete(4, 3);
445   EXPECT_STREQ(L"Hellorld", engine()->GetText().c_str());
446   EXPECT_TRUE(engine()->Undo());
447   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
448   engine()->Delete(5, 6);
449   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
450   EXPECT_TRUE(engine()->Undo());
451   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
452   EXPECT_TRUE(engine()->Redo());
453   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
454 
455   // Undo down to the limit.
456   EXPECT_TRUE(engine()->Undo());
457   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
458   EXPECT_TRUE(engine()->Undo());
459   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
460   EXPECT_TRUE(engine()->Undo());
461   EXPECT_STREQ(L"Hell", engine()->GetText().c_str());
462   EXPECT_FALSE(engine()->Undo());
463   EXPECT_STREQ(L"Hell", engine()->GetText().c_str());
464 }
465 
TEST_F(CFDE_TextEditEngineTest,GetIndexForPoint)466 TEST_F(CFDE_TextEditEngineTest, GetIndexForPoint) {
467   engine()->SetFontSize(10.0f);
468   engine()->Insert(0, L"Hello World");
469   EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f}));
470   EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
471   EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
472   EXPECT_EQ(1U, engine()->GetIndexForPoint({5.0f, 5.0f}));
473   EXPECT_EQ(2U, engine()->GetIndexForPoint({10.0f, 5.0f}));
474 }
475 
TEST_F(CFDE_TextEditEngineTest,GetIndexForPointLineWrap)476 TEST_F(CFDE_TextEditEngineTest, GetIndexForPointLineWrap) {
477   engine()->SetFontSize(10.0f);
478   engine()->Insert(0,
479                    L"A text long enough to span multiple lines and test "
480                    L"getting indexes on multi-line edits.");
481   EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f}));
482   EXPECT_EQ(87U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
483   EXPECT_EQ(18U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
484   EXPECT_EQ(19U, engine()->GetIndexForPoint({1.0f, 10.0f}));
485   EXPECT_EQ(1U, engine()->GetIndexForPoint({5.0f, 5.0f}));
486   EXPECT_EQ(2U, engine()->GetIndexForPoint({10.0f, 5.0f}));
487 }
488 
TEST_F(CFDE_TextEditEngineTest,GetIndexForPointSpaceAtEnd)489 TEST_F(CFDE_TextEditEngineTest, GetIndexForPointSpaceAtEnd) {
490   engine()->SetFontSize(10.0f);
491   engine()->Insert(0, L"Hello World ");
492   EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f}));
493   EXPECT_EQ(12U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
494   EXPECT_EQ(12U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
495 }
496 
TEST_F(CFDE_TextEditEngineTest,GetIndexForPointLineBreaks)497 TEST_F(CFDE_TextEditEngineTest, GetIndexForPointLineBreaks) {
498   engine()->SetFontSize(10.0f);
499   engine()->Insert(0, L"Hello\nWorld");
500   EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f}));
501   EXPECT_EQ(5U, engine()->GetIndexForPoint({999999.0f, 0.0f}));
502   EXPECT_EQ(6U, engine()->GetIndexForPoint({0.0f, 10.0f}));
503   EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
504 }
505 
TEST_F(CFDE_TextEditEngineTest,CanGenerateCharacterInfo)506 TEST_F(CFDE_TextEditEngineTest, CanGenerateCharacterInfo) {
507   RetainPtr<CFGAS_GEFont> font = engine()->GetFont();
508   ASSERT_TRUE(font);
509 
510   // Has font but no text.
511   EXPECT_FALSE(engine()->CanGenerateCharacterInfo());
512 
513   // Has font and text.
514   engine()->Insert(0, L"Hi!");
515   EXPECT_TRUE(engine()->CanGenerateCharacterInfo());
516 
517   // Has text but no font.
518   engine()->SetFont(nullptr);
519   EXPECT_FALSE(engine()->CanGenerateCharacterInfo());
520 
521   // Has no text and no font.
522   engine()->Clear();
523   EXPECT_FALSE(engine()->CanGenerateCharacterInfo());
524 }
525 
TEST_F(CFDE_TextEditEngineTest,GetCharacterInfo)526 TEST_F(CFDE_TextEditEngineTest, GetCharacterInfo) {
527   std::pair<int32_t, CFX_RectF> char_info;
528 
529   engine()->Insert(0, L"Hi!");
530   ASSERT_EQ(3U, engine()->GetLength());
531 
532   char_info = engine()->GetCharacterInfo(0);
533   EXPECT_EQ(0, char_info.first);
534   EXPECT_FLOAT_EQ(0.0f, char_info.second.Left());
535   EXPECT_FLOAT_EQ(0.0f, char_info.second.Top());
536   EXPECT_FLOAT_EQ(8.664f, char_info.second.Width());
537   EXPECT_FLOAT_EQ(12.0f, char_info.second.Height());
538 
539   char_info = engine()->GetCharacterInfo(1);
540   EXPECT_EQ(0, char_info.first);
541   EXPECT_FLOAT_EQ(8.664f, char_info.second.Left());
542   EXPECT_FLOAT_EQ(0.0f, char_info.second.Top());
543   EXPECT_FLOAT_EQ(3.324f, char_info.second.Width());
544   EXPECT_FLOAT_EQ(12.0f, char_info.second.Height());
545 
546   char_info = engine()->GetCharacterInfo(2);
547   EXPECT_EQ(0, char_info.first);
548   EXPECT_FLOAT_EQ(11.988f, char_info.second.Left());
549   EXPECT_FLOAT_EQ(0.0f, char_info.second.Top());
550   EXPECT_FLOAT_EQ(3.996f, char_info.second.Width());
551   EXPECT_FLOAT_EQ(12.0f, char_info.second.Height());
552 
553   // Allow retrieving the character info for the end of the text, as that
554   // information can be used to determine where to draw a cursor positioned at
555   // the end.
556   char_info = engine()->GetCharacterInfo(3);
557   EXPECT_EQ(0, char_info.first);
558   EXPECT_FLOAT_EQ(15.984, char_info.second.Left());
559   EXPECT_FLOAT_EQ(0.0f, char_info.second.Top());
560   EXPECT_FLOAT_EQ(0.0f, char_info.second.Width());
561   EXPECT_FLOAT_EQ(12.0f, char_info.second.Height());
562 }
563 
TEST_F(CFDE_TextEditEngineTest,BoundsForWordAt)564 TEST_F(CFDE_TextEditEngineTest, BoundsForWordAt) {
565   size_t start_idx;
566   size_t count;
567 
568   std::tie(start_idx, count) = engine()->BoundsForWordAt(100);
569   EXPECT_EQ(0U, start_idx);
570   EXPECT_EQ(0U, count);
571   engine()->SetSelection(start_idx, count);
572   EXPECT_STREQ(L"", engine()->GetSelectedText().c_str());
573 
574   engine()->Clear();
575   engine()->Insert(0, L"Hello");
576   std::tie(start_idx, count) = engine()->BoundsForWordAt(0);
577   EXPECT_EQ(0U, start_idx);
578   EXPECT_EQ(5U, count);
579   engine()->SetSelection(start_idx, count);
580   EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str());
581 
582   engine()->Clear();
583   engine()->Insert(0, L"Hello World");
584   std::tie(start_idx, count) = engine()->BoundsForWordAt(100);
585   EXPECT_EQ(0U, start_idx);
586   EXPECT_EQ(0U, count);
587   engine()->SetSelection(start_idx, count);
588   EXPECT_STREQ(L"", engine()->GetSelectedText().c_str());
589 
590   std::tie(start_idx, count) = engine()->BoundsForWordAt(0);
591   EXPECT_EQ(0U, start_idx);
592   EXPECT_EQ(5U, count);
593   engine()->SetSelection(start_idx, count);
594   EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str());
595 
596   std::tie(start_idx, count) = engine()->BoundsForWordAt(1);
597   EXPECT_EQ(0U, start_idx);
598   EXPECT_EQ(5U, count);
599   engine()->SetSelection(start_idx, count);
600   EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str());
601 
602   std::tie(start_idx, count) = engine()->BoundsForWordAt(4);
603   EXPECT_EQ(0U, start_idx);
604   EXPECT_EQ(5U, count);
605   engine()->SetSelection(start_idx, count);
606   EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str());
607 
608   // Select the space
609   std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
610   EXPECT_EQ(5U, start_idx);
611   EXPECT_EQ(1U, count);
612   engine()->SetSelection(start_idx, count);
613   EXPECT_STREQ(L" ", engine()->GetSelectedText().c_str());
614 
615   std::tie(start_idx, count) = engine()->BoundsForWordAt(6);
616   EXPECT_EQ(6U, start_idx);
617   EXPECT_EQ(5U, count);
618   engine()->SetSelection(start_idx, count);
619   EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
620 
621   engine()->Clear();
622   engine()->Insert(0, L"123 456 789");
623   std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
624   engine()->SetSelection(start_idx, count);
625   EXPECT_STREQ(L"456", engine()->GetSelectedText().c_str());
626 
627   engine()->Clear();
628   engine()->Insert(0, L"123def789");
629   std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
630   engine()->SetSelection(start_idx, count);
631   EXPECT_STREQ(L"123def789", engine()->GetSelectedText().c_str());
632 
633   engine()->Clear();
634   engine()->Insert(0, L"abc456ghi");
635   std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
636   engine()->SetSelection(start_idx, count);
637   EXPECT_STREQ(L"abc456ghi", engine()->GetSelectedText().c_str());
638 
639   engine()->Clear();
640   engine()->Insert(0, L"hello, world");
641   std::tie(start_idx, count) = engine()->BoundsForWordAt(0);
642   engine()->SetSelection(start_idx, count);
643   EXPECT_STREQ(L"hello", engine()->GetSelectedText().c_str());
644 
645   engine()->Clear();
646   engine()->Insert(0, L"hello, world");
647   std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
648   engine()->SetSelection(start_idx, count);
649   EXPECT_STREQ(L",", engine()->GetSelectedText().c_str());
650 
651   engine()->Clear();
652   engine()->Insert(0, L"np-complete");
653   std::tie(start_idx, count) = engine()->BoundsForWordAt(6);
654   engine()->SetSelection(start_idx, count);
655   EXPECT_STREQ(L"complete", engine()->GetSelectedText().c_str());
656 
657   engine()->Clear();
658   engine()->Insert(0, L"(123) 456-7890");
659   std::tie(start_idx, count) = engine()->BoundsForWordAt(0);
660   engine()->SetSelection(start_idx, count);
661   EXPECT_STREQ(L"(", engine()->GetSelectedText().c_str());
662 
663   std::tie(start_idx, count) = engine()->BoundsForWordAt(1);
664   engine()->SetSelection(start_idx, count);
665   EXPECT_STREQ(L"123", engine()->GetSelectedText().c_str());
666 
667   std::tie(start_idx, count) = engine()->BoundsForWordAt(7);
668   engine()->SetSelection(start_idx, count);
669   EXPECT_STREQ(L"456", engine()->GetSelectedText().c_str());
670 
671   std::tie(start_idx, count) = engine()->BoundsForWordAt(11);
672   engine()->SetSelection(start_idx, count);
673   EXPECT_STREQ(L"7890", engine()->GetSelectedText().c_str());
674 
675   // Tests from:
676   // http://unicode.org/Public/UNIDATA/auxiliary/WordBreakTest.html#samples
677   struct bounds {
678     size_t start;
679     size_t end;
680   };
681   struct {
682     const wchar_t* str;
683     std::vector<const wchar_t*> results;
684   } tests[] = {
685       // {L"\r\na\n\u0308", {L"\r\n", L"a", L"\n", L"\u0308"}},
686       // {L"a\u0308", {L"a\u0308"}},
687       // {L" \u200d\u0646", {L" \u200d", L"\u0646"}},
688       // {L"\u0646\u200d ", {L"\u0646\u200d", L" "}},
689       {L"AAA", {L"AAA"}},
690       {L"A:A", {L"A:A"}},
691       {L"A::A", {L"A", L":", L":", L"A"}},
692       // {L"\u05d0'", {L"\u05d0'"}},
693       // {L"\u05d0\"\u05d0", {L"\u05d0\"\u05d0"}},
694       {L"A00A", {L"A00A"}},
695       {L"0,0", {L"0,0"}},
696       {L"0,,0", {L"0", L",", L",", L"0"}},
697       {L"\u3031\u3031", {L"\u3031\u3031"}},
698       {L"A_0_\u3031_", {L"A_0_\u3031_"}},
699       {L"A__A", {L"A__A"}},
700       // {L"\u200d\u2640", {L"\u200d\u2640"}},
701       // {L"a\u0308\u200b\u0308b", {L"a\u0308\u200b\u0308b"}},
702   };
703 
704   for (auto t : tests) {
705     engine()->Clear();
706     engine()->Insert(0, t.str);
707 
708     size_t idx = 0;
709     for (const auto* res : t.results) {
710       std::tie(start_idx, count) = engine()->BoundsForWordAt(idx);
711       engine()->SetSelection(start_idx, count);
712       EXPECT_STREQ(res, engine()->GetSelectedText().c_str())
713           << "Input: '" << t.str << "'";
714       idx += count;
715     }
716   }
717 }
718 
TEST_F(CFDE_TextEditEngineTest,CursorMovement)719 TEST_F(CFDE_TextEditEngineTest, CursorMovement) {
720   engine()->Clear();
721   engine()->Insert(0, L"Hello");
722 
723   EXPECT_EQ(0U, engine()->GetIndexLeft(0));
724   EXPECT_EQ(5U, engine()->GetIndexRight(5));
725   EXPECT_EQ(2U, engine()->GetIndexUp(2));
726   EXPECT_EQ(2U, engine()->GetIndexDown(2));
727   EXPECT_EQ(1U, engine()->GetIndexLeft(2));
728   EXPECT_EQ(3U, engine()->GetIndexRight(2));
729   EXPECT_EQ(0U, engine()->GetIndexAtStartOfLine(2));
730   EXPECT_EQ(5U, engine()->GetIndexAtEndOfLine(2));
731 
732   engine()->Clear();
733   engine()->Insert(0, L"The book is \"مدخل إلى C++\"");
734   EXPECT_FALSE(FX_IsOdd(engine()->GetCharacterInfo(3).first));
735   EXPECT_EQ(2U, engine()->GetIndexLeft(3));
736   EXPECT_EQ(4U, engine()->GetIndexRight(3));
737   EXPECT_TRUE(FX_IsOdd(engine()->GetCharacterInfo(15).first));
738   EXPECT_EQ(14U, engine()->GetIndexLeft(15));
739   EXPECT_EQ(16U, engine()->GetIndexRight(15));
740   EXPECT_FALSE(FX_IsOdd(engine()->GetCharacterInfo(23).first));
741   EXPECT_EQ(22U, engine()->GetIndexLeft(23));
742   EXPECT_EQ(24U, engine()->GetIndexRight(23));
743 
744   engine()->Clear();
745   engine()->Insert(0, L"Hello\r\nWorld\r\nTest");
746   // Move to end of Hello from start of World.
747   engine()->SetSelection(engine()->GetIndexLeft(7U), 7);
748   EXPECT_STREQ(L"\r\nWorld", engine()->GetSelectedText().c_str());
749 
750   // Second letter in Hello from second letter in World.
751   engine()->SetSelection(engine()->GetIndexUp(8U), 2);
752   EXPECT_STREQ(L"el", engine()->GetSelectedText().c_str());
753 
754   // Second letter in World from second letter in Test.
755   engine()->SetSelection(engine()->GetIndexUp(15U), 2);
756   EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
757 
758   // Second letter in World from second letter in Hello.
759   engine()->SetSelection(engine()->GetIndexDown(1U), 2);
760   EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
761 
762   // Second letter in Test from second letter in World.
763   engine()->SetSelection(engine()->GetIndexDown(8U), 2);
764   EXPECT_STREQ(L"es", engine()->GetSelectedText().c_str());
765 
766   size_t start_idx = engine()->GetIndexAtStartOfLine(8U);
767   size_t end_idx = engine()->GetIndexAtEndOfLine(8U);
768   engine()->SetSelection(start_idx, end_idx - start_idx);
769   EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
770 
771   // Move past \r\n to before W.
772   engine()->SetSelection(engine()->GetIndexRight(5U), 5);
773   EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
774 
775   engine()->Clear();
776   engine()->Insert(0, L"Short\nAnd a very long line");
777   engine()->SetSelection(engine()->GetIndexUp(14U), 11);
778   EXPECT_STREQ(L"\nAnd a very", engine()->GetSelectedText().c_str());
779 
780   engine()->Clear();
781   engine()->Insert(0, L"A Very long line\nShort");
782   EXPECT_EQ(engine()->GetLength(), engine()->GetIndexDown(8U));
783 
784   engine()->Clear();
785   engine()->Insert(0, L"Hello\rWorld\rTest");
786   // Move to end of Hello from start of World.
787   engine()->SetSelection(engine()->GetIndexLeft(6U), 6);
788   EXPECT_STREQ(L"\rWorld", engine()->GetSelectedText().c_str());
789 
790   // Second letter in Hello from second letter in World.
791   engine()->SetSelection(engine()->GetIndexUp(7U), 2);
792   EXPECT_STREQ(L"el", engine()->GetSelectedText().c_str());
793 
794   // Second letter in World from second letter in Test.
795   engine()->SetSelection(engine()->GetIndexUp(13U), 2);
796   EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
797 
798   // Second letter in World from second letter in Hello.
799   engine()->SetSelection(engine()->GetIndexDown(1U), 2);
800   EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
801 
802   // Second letter in Test from second letter in World.
803   engine()->SetSelection(engine()->GetIndexDown(7U), 2);
804   EXPECT_STREQ(L"es", engine()->GetSelectedText().c_str());
805 
806   start_idx = engine()->GetIndexAtStartOfLine(7U);
807   end_idx = engine()->GetIndexAtEndOfLine(7U);
808   engine()->SetSelection(start_idx, end_idx - start_idx);
809   EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
810 
811   // Move past \r to before W.
812   engine()->SetSelection(engine()->GetIndexRight(5U), 5);
813   EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
814 
815   engine()->Clear();
816   engine()->Insert(0, L"Hello\nWorld\nTest");
817   // Move to end of Hello from start of World.
818   engine()->SetSelection(engine()->GetIndexLeft(6U), 6);
819   EXPECT_STREQ(L"\nWorld", engine()->GetSelectedText().c_str());
820 
821   // Second letter in Hello from second letter in World.
822   engine()->SetSelection(engine()->GetIndexUp(7U), 2);
823   EXPECT_STREQ(L"el", engine()->GetSelectedText().c_str());
824 
825   // Second letter in World from second letter in Test.
826   engine()->SetSelection(engine()->GetIndexUp(13U), 2);
827   EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
828 
829   // Second letter in World from second letter in Hello.
830   engine()->SetSelection(engine()->GetIndexDown(1U), 2);
831   EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
832 
833   // Second letter in Test from second letter in World.
834   engine()->SetSelection(engine()->GetIndexDown(7U), 2);
835   EXPECT_STREQ(L"es", engine()->GetSelectedText().c_str());
836 
837   start_idx = engine()->GetIndexAtStartOfLine(7U);
838   end_idx = engine()->GetIndexAtEndOfLine(7U);
839   engine()->SetSelection(start_idx, end_idx - start_idx);
840   EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
841 
842   // Move past \r to before W.
843   engine()->SetSelection(engine()->GetIndexRight(5U), 5);
844   EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
845 }
846