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