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