sticky_test.go
1package ygo_test
2
3import (
4 "unsafe"
5
6 "git.kilimanjaro.io/ygo"
7 "testing"
8)
9
10func TestStickyIndexBasic(t *testing.T) {
11 doc, err := ygo.NewDoc()
12 if err != nil {
13 t.Fatalf("failed to create doc: %v", err)
14 }
15 defer doc.Destroy()
16
17 txt, err := doc.GetText("test")
18 if err != nil {
19 t.Fatalf("failed to get text: %v", err)
20 }
21 defer txt.Destroy()
22
23 var length uint32
24 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
25 txt.Insert(txn, 0, "1")
26 txt.Insert(txn, 0, "abc")
27 txt.Insert(txn, 0, "z")
28 txt.Insert(txn, 0, "y")
29 txt.Insert(txn, 0, "x")
30
31 length = txt.Len(txn)
32
33 for i := uint32(0); i < length; i++ {
34 for _, assoc := range []ygo.Assoc{ygo.AssocBefore, ygo.AssocAfter} {
35 pos, err := ygo.NewStickyIndexFromIndex(txt, txn, i, assoc)
36 if err != nil {
37 return err
38 }
39 defer pos.Destroy()
40
41 // Test encode/decode
42 encoded, err := pos.Encode()
43 if err != nil {
44 return err
45 }
46 pos2, err := ygo.DecodeStickyIndex(encoded)
47 if err != nil {
48 return err
49 }
50 defer pos2.Destroy()
51
52 // Read position back
53 branch, idx, err := pos2.Read(txn)
54 if err != nil {
55 return err
56 }
57 if branch == nil {
58 t.Error("failed to read sticky index")
59 }
60 if idx != i {
61 t.Errorf("expected index %d, got %d", i, idx)
62 }
63 posAssoc, err := pos2.Assoc()
64 if err != nil {
65 return err
66 }
67 if posAssoc != assoc {
68 t.Errorf("expected assoc %d, got %d", assoc, posAssoc)
69 }
70 }
71 }
72 return nil
73 })
74 if err != nil {
75 t.Fatalf("transaction failed: %v", err)
76 }
77}
78
79func TestStickyIndexSurvivesChanges(t *testing.T) {
80 doc, err := ygo.NewDoc()
81 if err != nil {
82 t.Fatalf("failed to create doc: %v", err)
83 }
84 defer doc.Destroy()
85
86 txt, err := doc.GetText("test")
87 if err != nil {
88 t.Fatalf("failed to get text: %v", err)
89 }
90 defer txt.Destroy()
91
92 // Insert initial content
93 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
94 txt.Insert(txn, 0, "hello world")
95 return nil
96 })
97 if err != nil {
98 t.Fatalf("transaction failed: %v", err)
99 }
100
101 // Create sticky index at position 6 (before "world")
102 var pos *ygo.StickyIndex
103 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
104 var err error
105 pos, err = ygo.NewStickyIndexFromIndex(txt, txn, 6, ygo.AssocBefore)
106 return err
107 })
108 if err != nil {
109 t.Fatalf("failed to create sticky index: %v", err)
110 }
111 defer pos.Destroy()
112
113 // Insert text before the position
114 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
115 txt.Insert(txn, 0, "hi ")
116 return nil
117 })
118 if err != nil {
119 t.Fatalf("transaction failed: %v", err)
120 }
121
122 // Read position - should now be at index 9 (moved with content)
123 var branch unsafe.Pointer
124 var idx uint32
125 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
126 var err error
127 branch, idx, err = pos.Read(txn)
128 return err
129 })
130 if err != nil {
131 t.Fatalf("failed to read sticky index: %v", err)
132 }
133 if branch == nil {
134 t.Fatal("sticky index became invalid after insert")
135 }
136
137 // Verify the character at this position
138 var content string
139 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
140 var err error
141 content, err = txt.String(txn)
142 return err
143 })
144 if err != nil {
145 t.Fatalf("failed to get string: %v", err)
146 }
147
148 // "hi hello world" - position 9 should be 'w' in world
149 if len(content) > int(idx) && content[idx] != 'w' {
150 t.Errorf("expected 'w' at position %d, got '%c' in '%s'", idx, content[idx], content)
151 }
152}
153
154func TestStickyIndexJSON(t *testing.T) {
155 doc, err := ygo.NewDoc()
156 if err != nil {
157 t.Fatalf("failed to create doc: %v", err)
158 }
159 defer doc.Destroy()
160
161 txt, err := doc.GetText("test")
162 if err != nil {
163 t.Fatalf("failed to get text: %v", err)
164 }
165 defer txt.Destroy()
166
167 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
168 txt.Insert(txn, 0, "test content")
169 return nil
170 })
171 if err != nil {
172 t.Fatalf("transaction failed: %v", err)
173 }
174
175 // Create sticky index with write transaction
176 var pos *ygo.StickyIndex
177 err = doc.WithWriteTransaction(func(txn *ygo.Transaction) error {
178 var err error
179 pos, err = ygo.NewStickyIndexFromIndex(txt, txn, 5, ygo.AssocAfter)
180 return err
181 })
182 if err != nil {
183 t.Fatalf("failed to create sticky index: %v", err)
184 }
185 defer pos.Destroy()
186
187 // Test JSON serialization
188 jsonStr, err := pos.ToJSON()
189 if err != nil {
190 t.Fatalf("failed to get JSON: %v", err)
191 }
192 if jsonStr == "" {
193 t.Error("expected non-empty JSON string")
194 }
195
196 // Test JSON deserialization
197 pos2, err := ygo.ParseStickyIndexJSON(jsonStr)
198 if err != nil {
199 t.Fatalf("failed to parse sticky index from JSON: %v", err)
200 }
201 defer pos2.Destroy()
202
203 // Verify it works
204 var branch unsafe.Pointer
205 var idx uint32
206 err = doc.WithReadTransaction(func(txn *ygo.Transaction) error {
207 var err error
208 branch, idx, err = pos2.Read(txn)
209 return err
210 })
211 if err != nil {
212 t.Fatalf("failed to read sticky index from JSON: %v", err)
213 }
214 if branch == nil {
215 t.Error("failed to read sticky index from JSON")
216 }
217 if idx != 5 {
218 t.Errorf("expected index 5, got %d", idx)
219 }
220}