1package edit
2
3import (
4 "strings"
5 "testing"
6 "unicode/utf8"
7
8 "github.com/stretchr/testify/mock"
9)
10
11func TestGraphemesAndIME(t *testing.T) {
12 s := State{}
13 s.Sync("e\u0301👩<U+200D>💻a")
14 s.Move(len(s.Value), false)
15 s.Left(false)
16 if s.Cursor != len("e\u0301👩<U+200D>💻") {
17 t.Fatal(s.Cursor)
18 }
19 s.Left(false)
20 if s.Cursor != len("e\u0301") {
21 t.Fatal(s.Cursor)
22 }
23 s.Select(0, len("e\u0301"))
24 if !s.Delete(true, false) || s.Value != "👩<U+200D>💻a" {
25 t.Fatalf("%+v", s)
26 }
27 s.Select(len("👩<U+200D>💻"), len("👩<U+200D>💻"))
28 if !s.Done(IMEBatch{DeleteBefore: 1, Commit: "é"}) || s.Value != "éa" {
29 t.Fatalf("delete bytes: %+v", s)
30 }
31 s.Done(IMEBatch{Preedit: "候補", Begin: 0, End: len("候補")})
32 if s.Preedit != "候補" || s.Value != "éa" {
33 t.Fatal(s)
34 }
35 ime := NewMockIME(t)
36 ime.EXPECT().Enable().Once()
37 ime.EXPECT().Surrounding("éa", mock.Anything, mock.Anything).Maybe()
38 ime.EXPECT().CursorRect(mock.Anything, mock.Anything, mock.Anything, mock.Anything).Maybe()
39 ime.EXPECT().ContentType(false, false).Once()
40 ime.EXPECT().Disable().Once()
41 s.Focus(ime, false, false)
42 s.Blur(ime)
43 if s.Preedit != "" {
44 t.Fatal(s)
45 }
46}
47func TestClipboardAndMask(t *testing.T) {
48 s := State{}
49 s.Sync("s3crét")
50 s.Select(0, len(s.Value))
51 c := NewMockClipboard(t) // a masked copy must not write
52 if err := s.Copy(c, true); err != nil {
53 t.Fatal(err)
54 }
55 if s.Mask() != "••••••" {
56 t.Fatal(s.Mask())
57 }
58 read := func(data []byte) {
59 c.EXPECT().ReadText(mock.Anything).Return(data, nil).Once()
60 }
61 read([]byte{0xff})
62 if changed, _ := s.Paste(c); changed {
63 t.Fatal(s)
64 }
65 read([]byte(strings.Repeat("x", MaxClipboardBytes+1)))
66 if changed, _ := s.Paste(c); changed {
67 t.Fatal(s)
68 }
69 read([]byte("ok"))
70 if changed, _ := s.Paste(c); !changed || s.Value != "ok" {
71 t.Fatal(s)
72 }
73}
74func FuzzEdit(f *testing.F) {
75 f.Add([]byte("e\u0301👩<U+200D>💻🧪"))
76 f.Add([]byte{0xff, 0x80, 0x00})
77 f.Fuzz(func(t *testing.T, data []byte) {
78 if len(data) > 2048 {
79 return
80 }
81 var s State
82 for _, op := range data {
83 switch op % 7 {
84 case 0:
85 s.Left(false)
86 case 1:
87 s.Right(true)
88 case 2:
89 s.Insert("e\u0301")
90 case 3:
91 s.Insert("👩<U+200D>💻")
92 case 4:
93 s.Delete(true, false)
94 case 5:
95 s.Delete(false, true)
96 case 6:
97 s.Sync(strings.ToValidUTF8(string(data), "�"))
98 }
99 if !utf8.ValidString(s.Value) {
100 t.Fatal("invalid UTF-8")
101 }
102 for _, p := range []int{s.Cursor, s.Anchor} {
103 ok := false
104 for _, v := range Boundaries(s.Value) {
105 if p == v {
106 ok = true
107 }
108 }
109 if !ok {
110 t.Fatalf("not boundary %d in %q", p, s.Value)
111 }
112 }
113 }
114 })
115}