• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 PDFium 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 "xfa/fde/cfde_texteditengine.h"
6 
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "testing/test_support.h"
9 #include "third_party/base/ptr_util.h"
10 #include "xfa/fgas/font/cfgas_gefont.h"
11 
12 class CFDE_TextEditEngineTest : public testing::Test {
13  public:
14   class Delegate : public CFDE_TextEditEngine::Delegate {
15    public:
Reset()16     void Reset() {
17       text_is_full = false;
18       fail_validation = false;
19     }
20 
NotifyTextFull()21     void NotifyTextFull() override { text_is_full = true; }
22 
OnCaretChanged()23     void OnCaretChanged() override {}
OnTextChanged(const WideString & prevText)24     void OnTextChanged(const WideString& prevText) override {}
OnSelChanged()25     void OnSelChanged() override {}
OnValidate(const WideString & wsText)26     bool OnValidate(const WideString& wsText) override {
27       return !fail_validation;
28     }
SetScrollOffset(float fScrollOffset)29     void SetScrollOffset(float fScrollOffset) override {}
30 
31     bool fail_validation = false;
32     bool text_is_full = false;
33   };
34 
CFDE_TextEditEngineTest()35   CFDE_TextEditEngineTest() {}
~CFDE_TextEditEngineTest()36   ~CFDE_TextEditEngineTest() override {}
37 
SetUp()38   void SetUp() override {
39     font_ =
40         CFGAS_GEFont::LoadFont(L"Arial Black", 0, 0, GetGlobalFontManager());
41     ASSERT(font_.Get() != nullptr);
42 
43     engine_ = pdfium::MakeUnique<CFDE_TextEditEngine>();
44     engine_->SetFont(font_);
45     engine_->SetFontSize(12.0f);
46   }
47 
TearDown()48   void TearDown() override { engine_.reset(); }
49 
engine() const50   CFDE_TextEditEngine* engine() const { return engine_.get(); }
51 
52  private:
53   RetainPtr<CFGAS_GEFont> font_;
54   std::unique_ptr<CFDE_TextEditEngine> engine_;
55 };
56 
TEST_F(CFDE_TextEditEngineTest,Insert)57 TEST_F(CFDE_TextEditEngineTest, Insert) {
58   EXPECT_STREQ(L"", engine()->GetText().c_str());
59 
60   engine()->Insert(0, L"");
61   EXPECT_STREQ(L"", engine()->GetText().c_str());
62   EXPECT_EQ(0U, engine()->GetLength());
63 
64   engine()->Insert(0, L"Hello");
65   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
66   EXPECT_EQ(5U, engine()->GetLength());
67 
68   engine()->Insert(5, L" World");
69   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
70   EXPECT_EQ(11U, engine()->GetLength());
71 
72   engine()->Insert(5, L" New");
73   EXPECT_STREQ(L"Hello New World", engine()->GetText().c_str());
74 
75   engine()->Insert(100, L" Cat");
76   EXPECT_STREQ(L"Hello New World Cat", engine()->GetText().c_str());
77 
78   engine()->Clear();
79 
80   engine()->SetHasCharacterLimit(true);
81   engine()->SetCharacterLimit(5);
82   engine()->Insert(0, L"Hello");
83 
84   // No delegate
85   engine()->Insert(5, L" World");
86   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
87 
88   engine()->SetCharacterLimit(8);
89   engine()->Insert(5, L" World");
90   EXPECT_STREQ(L"Hello Wo", engine()->GetText().c_str());
91 
92   engine()->Clear();
93 
94   // With Delegate
95   auto delegate = pdfium::MakeUnique<CFDE_TextEditEngineTest::Delegate>();
96   engine()->SetDelegate(delegate.get());
97 
98   engine()->SetCharacterLimit(5);
99   engine()->Insert(0, L"Hello");
100 
101   // Insert when full.
102   engine()->Insert(5, L" World");
103   EXPECT_TRUE(delegate->text_is_full);
104   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
105   delegate->Reset();
106 
107   engine()->SetCharacterLimit(8);
108   engine()->Insert(5, L" World");
109   EXPECT_TRUE(delegate->text_is_full);
110   EXPECT_STREQ(L"Hello Wo", engine()->GetText().c_str());
111   delegate->Reset();
112   engine()->SetHasCharacterLimit(false);
113 
114   engine()->Clear();
115   engine()->Insert(0, L"Hello");
116 
117   // Insert Invalid text
118   delegate->fail_validation = true;
119   engine()->EnableValidation(true);
120   engine()->Insert(5, L" World");
121   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
122 
123   delegate->fail_validation = false;
124   engine()->Insert(5, L" World");
125   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
126   engine()->EnableValidation(false);
127 
128   engine()->Clear();
129 
130   engine()->Insert(0, L"Hello\nWorld");
131   EXPECT_FALSE(delegate->text_is_full);
132   EXPECT_STREQ(L"Hello\nWorld", engine()->GetText().c_str());
133   delegate->Reset();
134   engine()->Clear();
135 
136   // Insert with limited area and over-fill
137   engine()->LimitHorizontalScroll(true);
138   engine()->SetAvailableWidth(60.0f);  // Fits 'Hello Wo'.
139   engine()->Insert(0, L"Hello");
140   EXPECT_FALSE(delegate->text_is_full);
141   engine()->Insert(5, L" World");
142   EXPECT_TRUE(delegate->text_is_full);
143   EXPECT_STREQ(L"Hello Wo", engine()->GetText().c_str());
144   engine()->LimitHorizontalScroll(false);
145 
146   delegate->Reset();
147   engine()->Clear();
148 
149   engine()->SetLineSpace(12.0f);
150   engine()->LimitVerticalScroll(true);
151   // Default is one line of text.
152   engine()->Insert(0, L"Hello");
153   EXPECT_FALSE(delegate->text_is_full);
154   engine()->Insert(5, L" Wo\nrld");
155   EXPECT_TRUE(delegate->text_is_full);
156   EXPECT_STREQ(L"Hello Wo\n", engine()->GetText().c_str());
157   engine()->LimitVerticalScroll(false);
158 
159   engine()->SetDelegate(nullptr);
160 }
161 
TEST_F(CFDE_TextEditEngineTest,Delete)162 TEST_F(CFDE_TextEditEngineTest, Delete) {
163   EXPECT_STREQ(L"", engine()->Delete(0, 50).c_str());
164   EXPECT_STREQ(L"", engine()->GetText().c_str());
165 
166   engine()->Insert(0, L"Hello World");
167   EXPECT_STREQ(L" World", engine()->Delete(5, 6).c_str());
168   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
169 
170   engine()->Clear();
171   engine()->Insert(0, L"Hello World");
172   EXPECT_STREQ(L" ", engine()->Delete(5, 1).c_str());
173   EXPECT_STREQ(L"HelloWorld", engine()->GetText().c_str());
174 
175   EXPECT_STREQ(L"elloWorld", engine()->Delete(1, 50).c_str());
176   EXPECT_STREQ(L"H", engine()->GetText().c_str());
177 }
178 
TEST_F(CFDE_TextEditEngineTest,Clear)179 TEST_F(CFDE_TextEditEngineTest, Clear) {
180   EXPECT_STREQ(L"", engine()->GetText().c_str());
181 
182   engine()->Clear();
183   EXPECT_STREQ(L"", engine()->GetText().c_str());
184 
185   engine()->Insert(0, L"Hello World");
186   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
187 
188   engine()->Clear();
189   EXPECT_STREQ(L"", engine()->GetText().c_str());
190   EXPECT_EQ(0U, engine()->GetLength());
191 }
192 
TEST_F(CFDE_TextEditEngineTest,GetChar)193 TEST_F(CFDE_TextEditEngineTest, GetChar) {
194   // Out of bounds.
195   EXPECT_EQ(L'\0', engine()->GetChar(0));
196 
197   engine()->Insert(0, L"Hello World");
198   EXPECT_EQ(L'H', engine()->GetChar(0));
199   EXPECT_EQ(L'd', engine()->GetChar(engine()->GetLength() - 1));
200   EXPECT_EQ(L' ', engine()->GetChar(5));
201 
202   engine()->Insert(5, L" A");
203   EXPECT_STREQ(L"Hello A World", engine()->GetText().c_str());
204   EXPECT_EQ(L'W', engine()->GetChar(8));
205 
206   engine()->EnablePasswordMode(true);
207   EXPECT_EQ(L'*', engine()->GetChar(8));
208 
209   engine()->SetAliasChar(L'+');
210   EXPECT_EQ(L'+', engine()->GetChar(8));
211 }
212 
TEST_F(CFDE_TextEditEngineTest,GetWidthOfChar)213 TEST_F(CFDE_TextEditEngineTest, GetWidthOfChar) {
214   // Out of Bounds.
215   EXPECT_EQ(0U, engine()->GetWidthOfChar(0));
216 
217   engine()->Insert(0, L"Hello World");
218   EXPECT_EQ(199920U, engine()->GetWidthOfChar(0));
219   EXPECT_EQ(159840U, engine()->GetWidthOfChar(1));
220 
221   engine()->Insert(0, L"\t");
222   EXPECT_EQ(0U, engine()->GetWidthOfChar(0));
223 }
224 
TEST_F(CFDE_TextEditEngineTest,GetDisplayPos)225 TEST_F(CFDE_TextEditEngineTest, GetDisplayPos) {
226   EXPECT_EQ(0U, engine()->GetDisplayPos(FDE_TEXTEDITPIECE()).size());
227 }
228 
TEST_F(CFDE_TextEditEngineTest,Selection)229 TEST_F(CFDE_TextEditEngineTest, Selection) {
230   EXPECT_FALSE(engine()->HasSelection());
231   engine()->SelectAll();
232   EXPECT_FALSE(engine()->HasSelection());
233 
234   engine()->Insert(0, L"Hello World");
235   EXPECT_STREQ(L"", engine()->DeleteSelectedText().c_str());
236 
237   EXPECT_FALSE(engine()->HasSelection());
238   engine()->SelectAll();
239   EXPECT_TRUE(engine()->HasSelection());
240   EXPECT_STREQ(L"Hello World", engine()->GetSelectedText().c_str());
241 
242   engine()->ClearSelection();
243   EXPECT_FALSE(engine()->HasSelection());
244   EXPECT_STREQ(L"", engine()->GetSelectedText().c_str());
245 
246   engine()->SelectAll();
247   size_t start_idx;
248   size_t count;
249   std::tie(start_idx, count) = engine()->GetSelection();
250   EXPECT_EQ(0U, start_idx);
251   EXPECT_EQ(11U, count);
252 
253   // Selection before gap.
254   EXPECT_STREQ(L"Hello World", engine()->GetSelectedText().c_str());
255   EXPECT_TRUE(engine()->HasSelection());
256   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
257 
258   engine()->Insert(5, L" A");
259   EXPECT_FALSE(engine()->HasSelection());
260   EXPECT_STREQ(L"", engine()->GetSelectedText().c_str());
261 
262   // Selection over the gap.
263   engine()->SelectAll();
264   EXPECT_TRUE(engine()->HasSelection());
265   EXPECT_STREQ(L"Hello A World", engine()->GetSelectedText().c_str());
266   engine()->Clear();
267 
268   engine()->Insert(0, L"Hello World");
269   engine()->SelectAll();
270 
271   EXPECT_STREQ(L"Hello World", engine()->DeleteSelectedText().c_str());
272   EXPECT_FALSE(engine()->HasSelection());
273   EXPECT_STREQ(L"", engine()->GetText().c_str());
274 
275   engine()->Insert(0, L"Hello World");
276   engine()->SetSelection(5, 5);
277   EXPECT_STREQ(L" Worl", engine()->DeleteSelectedText().c_str());
278   EXPECT_FALSE(engine()->HasSelection());
279   EXPECT_STREQ(L"Hellod", engine()->GetText().c_str());
280 
281   engine()->Clear();
282   engine()->Insert(0, L"Hello World");
283   engine()->SelectAll();
284   engine()->ReplaceSelectedText(L"Goodbye Everybody");
285   EXPECT_FALSE(engine()->HasSelection());
286   EXPECT_STREQ(L"Goodbye Everybody", engine()->GetText().c_str());
287 
288   engine()->Clear();
289   engine()->Insert(0, L"Hello World");
290   engine()->SetSelection(1, 4);
291   engine()->ReplaceSelectedText(L"i,");
292   EXPECT_FALSE(engine()->HasSelection());
293   EXPECT_STREQ(L"Hi, World", engine()->GetText().c_str());
294 
295   // Selection fully after gap.
296   engine()->Clear();
297   engine()->Insert(0, L"Hello");
298   engine()->Insert(0, L"A ");
299   engine()->SetSelection(3, 6);
300   EXPECT_STREQ(L"ello", engine()->GetSelectedText().c_str());
301 
302   engine()->Clear();
303   engine()->Insert(0, L"Hello World");
304   engine()->ClearSelection();
305   engine()->DeleteSelectedText();
306   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
307 }
308 
TEST_F(CFDE_TextEditEngineTest,UndoRedo)309 TEST_F(CFDE_TextEditEngineTest, UndoRedo) {
310   EXPECT_FALSE(engine()->CanUndo());
311   EXPECT_FALSE(engine()->CanRedo());
312   EXPECT_FALSE(engine()->Undo());
313   EXPECT_FALSE(engine()->Redo());
314 
315   engine()->Insert(0, L"Hello");
316   EXPECT_TRUE(engine()->CanUndo());
317   EXPECT_FALSE(engine()->CanRedo());
318   EXPECT_TRUE(engine()->Undo());
319   EXPECT_STREQ(L"", engine()->GetText().c_str());
320   EXPECT_FALSE(engine()->CanUndo());
321   EXPECT_TRUE(engine()->CanRedo());
322   EXPECT_TRUE(engine()->Redo());
323   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
324   EXPECT_TRUE(engine()->CanUndo());
325   EXPECT_FALSE(engine()->CanRedo());
326 
327   engine()->Clear();
328   EXPECT_FALSE(engine()->CanUndo());
329   EXPECT_FALSE(engine()->CanRedo());
330 
331   engine()->Insert(0, L"Hello World");
332   engine()->SelectAll();
333   engine()->DeleteSelectedText();
334   EXPECT_STREQ(L"", engine()->GetText().c_str());
335   EXPECT_TRUE(engine()->CanUndo());
336   EXPECT_TRUE(engine()->Undo());
337   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
338   EXPECT_TRUE(engine()->CanRedo());
339   EXPECT_TRUE(engine()->Redo());
340   EXPECT_STREQ(L"", engine()->GetText().c_str());
341   EXPECT_TRUE(engine()->CanUndo());
342   EXPECT_FALSE(engine()->CanRedo());
343 
344   engine()->Insert(0, L"Hello World");
345   engine()->SelectAll();
346   engine()->ReplaceSelectedText(L"Goodbye Friend");
347   EXPECT_STREQ(L"Goodbye Friend", engine()->GetText().c_str());
348   EXPECT_TRUE(engine()->CanUndo());
349   EXPECT_TRUE(engine()->Undo());
350   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
351   EXPECT_TRUE(engine()->CanRedo());
352   EXPECT_TRUE(engine()->Redo());
353   EXPECT_STREQ(L"Goodbye Friend", engine()->GetText().c_str());
354 
355   engine()->Clear();
356   engine()->SetMaxEditOperationsForTesting(3);
357   engine()->Insert(0, L"First ");
358   engine()->Insert(engine()->GetLength(), L"Second ");
359   engine()->Insert(engine()->GetLength(), L"Third");
360 
361   EXPECT_TRUE(engine()->CanUndo());
362   EXPECT_TRUE(engine()->Undo());
363   EXPECT_STREQ(L"First Second ", engine()->GetText().c_str());
364   EXPECT_TRUE(engine()->CanUndo());
365   EXPECT_TRUE(engine()->Undo());
366   EXPECT_FALSE(
367       engine()->CanUndo());  // Can't undo First; undo buffer too small.
368   EXPECT_STREQ(L"First ", engine()->GetText().c_str());
369 
370   EXPECT_TRUE(engine()->CanRedo());
371   EXPECT_TRUE(engine()->Redo());
372   EXPECT_TRUE(engine()->CanRedo());
373   EXPECT_TRUE(engine()->Redo());
374   EXPECT_FALSE(engine()->CanRedo());
375   EXPECT_STREQ(L"First Second Third", engine()->GetText().c_str());
376 
377   engine()->Clear();
378 
379   engine()->SetMaxEditOperationsForTesting(4);
380 
381   // Go beyond the max operations limit.
382   engine()->Insert(0, L"H");
383   engine()->Insert(1, L"e");
384   engine()->Insert(2, L"l");
385   engine()->Insert(3, L"l");
386   engine()->Insert(4, L"o");
387   engine()->Insert(5, L" World");
388   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
389 
390   // Do A, undo. Do B, undo. Redo should cause B.
391   engine()->Delete(4, 3);
392   EXPECT_STREQ(L"Hellorld", engine()->GetText().c_str());
393   EXPECT_TRUE(engine()->Undo());
394   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
395   engine()->Delete(5, 6);
396   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
397   EXPECT_TRUE(engine()->Undo());
398   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
399   EXPECT_TRUE(engine()->Redo());
400   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
401 
402   // Undo down to the limit.
403   EXPECT_TRUE(engine()->Undo());
404   EXPECT_STREQ(L"Hello World", engine()->GetText().c_str());
405   EXPECT_TRUE(engine()->Undo());
406   EXPECT_STREQ(L"Hello", engine()->GetText().c_str());
407   EXPECT_TRUE(engine()->Undo());
408   EXPECT_STREQ(L"Hell", engine()->GetText().c_str());
409   EXPECT_FALSE(engine()->Undo());
410   EXPECT_STREQ(L"Hell", engine()->GetText().c_str());
411 }
412 
TEST_F(CFDE_TextEditEngineTest,GetIndexForPoint)413 TEST_F(CFDE_TextEditEngineTest, GetIndexForPoint) {
414   engine()->SetFontSize(10.0f);
415   engine()->Insert(0, L"Hello World");
416   EXPECT_EQ(0U, engine()->GetIndexForPoint({0.0f, 0.0f}));
417   EXPECT_EQ(11U, engine()->GetIndexForPoint({999999.0f, 9999999.0f}));
418   EXPECT_EQ(1U, engine()->GetIndexForPoint({10.0f, 5.0f}));
419 }
420 
TEST_F(CFDE_TextEditEngineTest,BoundsForWordAt)421 TEST_F(CFDE_TextEditEngineTest, BoundsForWordAt) {
422   size_t start_idx;
423   size_t count;
424 
425   std::tie(start_idx, count) = engine()->BoundsForWordAt(100);
426   EXPECT_EQ(0U, start_idx);
427   EXPECT_EQ(0U, count);
428   engine()->SetSelection(start_idx, count);
429   EXPECT_STREQ(L"", engine()->GetSelectedText().c_str());
430 
431   engine()->Clear();
432   engine()->Insert(0, L"Hello");
433   std::tie(start_idx, count) = engine()->BoundsForWordAt(0);
434   EXPECT_EQ(0U, start_idx);
435   EXPECT_EQ(5U, count);
436   engine()->SetSelection(start_idx, count);
437   EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str());
438 
439   engine()->Clear();
440   engine()->Insert(0, L"Hello World");
441   std::tie(start_idx, count) = engine()->BoundsForWordAt(100);
442   EXPECT_EQ(0U, start_idx);
443   EXPECT_EQ(0U, count);
444   engine()->SetSelection(start_idx, count);
445   EXPECT_STREQ(L"", engine()->GetSelectedText().c_str());
446 
447   std::tie(start_idx, count) = engine()->BoundsForWordAt(0);
448   EXPECT_EQ(0U, start_idx);
449   EXPECT_EQ(5U, count);
450   engine()->SetSelection(start_idx, count);
451   EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str());
452 
453   std::tie(start_idx, count) = engine()->BoundsForWordAt(1);
454   EXPECT_EQ(0U, start_idx);
455   EXPECT_EQ(5U, count);
456   engine()->SetSelection(start_idx, count);
457   EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str());
458 
459   std::tie(start_idx, count) = engine()->BoundsForWordAt(4);
460   EXPECT_EQ(0U, start_idx);
461   EXPECT_EQ(5U, count);
462   engine()->SetSelection(start_idx, count);
463   EXPECT_STREQ(L"Hello", engine()->GetSelectedText().c_str());
464 
465   // Select the space
466   std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
467   EXPECT_EQ(5U, start_idx);
468   EXPECT_EQ(1U, count);
469   engine()->SetSelection(start_idx, count);
470   EXPECT_STREQ(L" ", engine()->GetSelectedText().c_str());
471 
472   std::tie(start_idx, count) = engine()->BoundsForWordAt(6);
473   EXPECT_EQ(6U, start_idx);
474   EXPECT_EQ(5U, count);
475   engine()->SetSelection(start_idx, count);
476   EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
477 
478   engine()->Clear();
479   engine()->Insert(0, L"123 456 789");
480   std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
481   engine()->SetSelection(start_idx, count);
482   EXPECT_STREQ(L"456", engine()->GetSelectedText().c_str());
483 
484   engine()->Clear();
485   engine()->Insert(0, L"123def789");
486   std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
487   engine()->SetSelection(start_idx, count);
488   EXPECT_STREQ(L"123def789", engine()->GetSelectedText().c_str());
489 
490   engine()->Clear();
491   engine()->Insert(0, L"abc456ghi");
492   std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
493   engine()->SetSelection(start_idx, count);
494   EXPECT_STREQ(L"abc456ghi", engine()->GetSelectedText().c_str());
495 
496   engine()->Clear();
497   engine()->Insert(0, L"hello, world");
498   std::tie(start_idx, count) = engine()->BoundsForWordAt(0);
499   engine()->SetSelection(start_idx, count);
500   EXPECT_STREQ(L"hello", engine()->GetSelectedText().c_str());
501 
502   engine()->Clear();
503   engine()->Insert(0, L"hello, world");
504   std::tie(start_idx, count) = engine()->BoundsForWordAt(5);
505   engine()->SetSelection(start_idx, count);
506   EXPECT_STREQ(L",", engine()->GetSelectedText().c_str());
507 
508   engine()->Clear();
509   engine()->Insert(0, L"np-complete");
510   std::tie(start_idx, count) = engine()->BoundsForWordAt(6);
511   engine()->SetSelection(start_idx, count);
512   EXPECT_STREQ(L"complete", engine()->GetSelectedText().c_str());
513 
514   engine()->Clear();
515   engine()->Insert(0, L"(123) 456-7890");
516   std::tie(start_idx, count) = engine()->BoundsForWordAt(0);
517   engine()->SetSelection(start_idx, count);
518   EXPECT_STREQ(L"(", engine()->GetSelectedText().c_str());
519 
520   std::tie(start_idx, count) = engine()->BoundsForWordAt(1);
521   engine()->SetSelection(start_idx, count);
522   EXPECT_STREQ(L"123", engine()->GetSelectedText().c_str());
523 
524   std::tie(start_idx, count) = engine()->BoundsForWordAt(7);
525   engine()->SetSelection(start_idx, count);
526   EXPECT_STREQ(L"456", engine()->GetSelectedText().c_str());
527 
528   std::tie(start_idx, count) = engine()->BoundsForWordAt(11);
529   engine()->SetSelection(start_idx, count);
530   EXPECT_STREQ(L"7890", engine()->GetSelectedText().c_str());
531 
532   // Tests from:
533   // http://unicode.org/Public/UNIDATA/auxiliary/WordBreakTest.html#samples
534   struct bounds {
535     size_t start;
536     size_t end;
537   };
538   struct {
539     const wchar_t* str;
540     std::vector<const wchar_t*> results;
541   } tests[] = {
542       // {L"\r\na\n\u0308", {L"\r\n", L"a", L"\n", L"\u0308"}},
543       // {L"a\u0308", {L"a\u0308"}},
544       // {L" \u200d\u0646", {L" \u200d", L"\u0646"}},
545       // {L"\u0646\u200d ", {L"\u0646\u200d", L" "}},
546       {L"AAA", {L"AAA"}},
547       {L"A:A", {L"A:A"}},
548       {L"A::A", {L"A", L":", L":", L"A"}},
549       // {L"\u05d0'", {L"\u05d0'"}},
550       // {L"\u05d0\"\u05d0", {L"\u05d0\"\u05d0"}},
551       {L"A00A", {L"A00A"}},
552       {L"0,0", {L"0,0"}},
553       {L"0,,0", {L"0", L",", L",", L"0"}},
554       {L"\u3031\u3031", {L"\u3031\u3031"}},
555       {L"A_0_\u3031_", {L"A_0_\u3031_"}},
556       {L"A__A", {L"A__A"}},
557       // {L"\u200d\u2640", {L"\u200d\u2640"}},
558       // {L"a\u0308\u200b\u0308b", {L"a\u0308\u200b\u0308b"}},
559   };
560 
561   for (auto t : tests) {
562     engine()->Clear();
563     engine()->Insert(0, t.str);
564 
565     size_t idx = 0;
566     for (const auto* res : t.results) {
567       std::tie(start_idx, count) = engine()->BoundsForWordAt(idx);
568       engine()->SetSelection(start_idx, count);
569       EXPECT_STREQ(res, engine()->GetSelectedText().c_str())
570           << "Input: '" << t.str << "'";
571       idx += count;
572     }
573   }
574 }
575 
TEST_F(CFDE_TextEditEngineTest,CursorMovement)576 TEST_F(CFDE_TextEditEngineTest, CursorMovement) {
577   engine()->Clear();
578   engine()->Insert(0, L"Hello");
579 
580   EXPECT_EQ(0U, engine()->GetIndexLeft(0));
581   EXPECT_EQ(5U, engine()->GetIndexRight(5));
582   EXPECT_EQ(2U, engine()->GetIndexUp(2));
583   EXPECT_EQ(2U, engine()->GetIndexDown(2));
584   EXPECT_EQ(1U, engine()->GetIndexLeft(2));
585   EXPECT_EQ(1U, engine()->GetIndexBefore(2));
586   EXPECT_EQ(3U, engine()->GetIndexRight(2));
587   EXPECT_EQ(0U, engine()->GetIndexAtStartOfLine(2));
588   EXPECT_EQ(5U, engine()->GetIndexAtEndOfLine(2));
589 
590   engine()->Clear();
591   engine()->Insert(0, L"The book is \"مدخل إلى C++\"");
592   EXPECT_EQ(2U, engine()->GetIndexBefore(3));    // Before is to left.
593   EXPECT_EQ(16U, engine()->GetIndexBefore(15));  // Before is to right.
594   EXPECT_EQ(22U, engine()->GetIndexBefore(23));  // Before is to left.
595 
596   engine()->Clear();
597   engine()->Insert(0, L"Hello\r\nWorld\r\nTest");
598   // Move to end of Hello from start of World.
599   engine()->SetSelection(engine()->GetIndexBefore(7U), 7);
600   EXPECT_STREQ(L"\r\nWorld", engine()->GetSelectedText().c_str());
601 
602   // Second letter in Hello from second letter in World.
603   engine()->SetSelection(engine()->GetIndexUp(8U), 2);
604   EXPECT_STREQ(L"el", engine()->GetSelectedText().c_str());
605 
606   // Second letter in World from second letter in Test.
607   engine()->SetSelection(engine()->GetIndexUp(15U), 2);
608   EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
609 
610   // Second letter in World from second letter in Hello.
611   engine()->SetSelection(engine()->GetIndexDown(1U), 2);
612   EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
613 
614   // Second letter in Test from second letter in World.
615   engine()->SetSelection(engine()->GetIndexDown(8U), 2);
616   EXPECT_STREQ(L"es", engine()->GetSelectedText().c_str());
617 
618   size_t start_idx = engine()->GetIndexAtStartOfLine(8U);
619   size_t end_idx = engine()->GetIndexAtEndOfLine(8U);
620   engine()->SetSelection(start_idx, end_idx - start_idx);
621   EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
622 
623   // Move past \r\n to before W.
624   engine()->SetSelection(engine()->GetIndexRight(5U), 5);
625   EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
626 
627   engine()->Clear();
628   engine()->Insert(0, L"Short\nAnd a very long line");
629   engine()->SetSelection(engine()->GetIndexUp(14U), 11);
630   EXPECT_STREQ(L"\nAnd a very", engine()->GetSelectedText().c_str());
631 
632   engine()->Clear();
633   engine()->Insert(0, L"A Very long line\nShort");
634   EXPECT_EQ(engine()->GetLength(), engine()->GetIndexDown(8U));
635 
636   engine()->Clear();
637   engine()->Insert(0, L"Hello\rWorld\rTest");
638   // Move to end of Hello from start of World.
639   engine()->SetSelection(engine()->GetIndexBefore(6U), 6);
640   EXPECT_STREQ(L"\rWorld", engine()->GetSelectedText().c_str());
641 
642   // Second letter in Hello from second letter in World.
643   engine()->SetSelection(engine()->GetIndexUp(7U), 2);
644   EXPECT_STREQ(L"el", engine()->GetSelectedText().c_str());
645 
646   // Second letter in World from second letter in Test.
647   engine()->SetSelection(engine()->GetIndexUp(13U), 2);
648   EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
649 
650   // Second letter in World from second letter in Hello.
651   engine()->SetSelection(engine()->GetIndexDown(1U), 2);
652   EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
653 
654   // Second letter in Test from second letter in World.
655   engine()->SetSelection(engine()->GetIndexDown(7U), 2);
656   EXPECT_STREQ(L"es", engine()->GetSelectedText().c_str());
657 
658   start_idx = engine()->GetIndexAtStartOfLine(7U);
659   end_idx = engine()->GetIndexAtEndOfLine(7U);
660   engine()->SetSelection(start_idx, end_idx - start_idx);
661   EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
662 
663   // Move past \r to before W.
664   engine()->SetSelection(engine()->GetIndexRight(5U), 5);
665   EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
666 
667   engine()->Clear();
668   engine()->Insert(0, L"Hello\nWorld\nTest");
669   // Move to end of Hello from start of World.
670   engine()->SetSelection(engine()->GetIndexBefore(6U), 6);
671   EXPECT_STREQ(L"\nWorld", engine()->GetSelectedText().c_str());
672 
673   // Second letter in Hello from second letter in World.
674   engine()->SetSelection(engine()->GetIndexUp(7U), 2);
675   EXPECT_STREQ(L"el", engine()->GetSelectedText().c_str());
676 
677   // Second letter in World from second letter in Test.
678   engine()->SetSelection(engine()->GetIndexUp(13U), 2);
679   EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
680 
681   // Second letter in World from second letter in Hello.
682   engine()->SetSelection(engine()->GetIndexDown(1U), 2);
683   EXPECT_STREQ(L"or", engine()->GetSelectedText().c_str());
684 
685   // Second letter in Test from second letter in World.
686   engine()->SetSelection(engine()->GetIndexDown(7U), 2);
687   EXPECT_STREQ(L"es", engine()->GetSelectedText().c_str());
688 
689   start_idx = engine()->GetIndexAtStartOfLine(7U);
690   end_idx = engine()->GetIndexAtEndOfLine(7U);
691   engine()->SetSelection(start_idx, end_idx - start_idx);
692   EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
693 
694   // Move past \r to before W.
695   engine()->SetSelection(engine()->GetIndexRight(5U), 5);
696   EXPECT_STREQ(L"World", engine()->GetSelectedText().c_str());
697 }
698