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}