Skip to content

Commit c13c0e0

Browse files
committed
better tests
1 parent b159766 commit c13c0e0

File tree

3 files changed

+224
-227
lines changed

3 files changed

+224
-227
lines changed

lite_persist_test.go

Lines changed: 0 additions & 78 deletions
This file was deleted.

persist_test.go

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package bart
2+
3+
import (
4+
"math/rand/v2"
5+
"testing"
6+
)
7+
8+
// testVal as a simple value type.
9+
type testVal struct {
10+
Data int
11+
}
12+
13+
// Clone ensures deep copying for use with ...Persist.
14+
//
15+
// We use *testVal as the generic payload V,
16+
// which is a pointer type, so it must implement bart.Cloner[V]
17+
func (v *testVal) Clone() *testVal {
18+
if v == nil {
19+
return nil
20+
}
21+
return &testVal{Data: v.Data}
22+
}
23+
24+
// ########## Table[V] ##############
25+
26+
func TestInsertPersistTable(t *testing.T) {
27+
t.Parallel()
28+
29+
// setup
30+
const n = 10_000
31+
32+
prng := rand.New(rand.NewPCG(42, 42))
33+
pfxs := randomRealWorldPrefixes(prng, n)
34+
35+
orig := new(Table[*testVal])
36+
for _, pfx := range pfxs {
37+
orig.Insert(pfx, &testVal{Data: 1})
38+
}
39+
40+
clone := orig
41+
for _, pfx := range pfxs {
42+
clone := clone.InsertPersist(pfx, &testVal{Data: 2})
43+
44+
// mutate clone's value to ensure it's not aliased
45+
v2, _ := clone.Get(pfx)
46+
v2.Data = 3
47+
48+
// original must be unchanged
49+
v1, _ := orig.Get(pfx)
50+
if v1.Data != 1 {
51+
t.Errorf("InsertPersist: original table modified for prefix %s: want %q, got %q", pfx, 1, v1.Data)
52+
}
53+
54+
// cloned table should have the mutated value
55+
if v2.Data != 3 {
56+
t.Errorf("InsertPersist: mutated value not reflected for prefix %s", pfx)
57+
}
58+
59+
// ensure no aliasing
60+
if v1 == v2 {
61+
t.Errorf("InsertPersist: pointer aliasing detected for prefix %s", pfx)
62+
}
63+
}
64+
}
65+
66+
func TestUpdatePersistTable(t *testing.T) {
67+
t.Parallel()
68+
69+
// setup
70+
const n = 10_000
71+
72+
prng := rand.New(rand.NewPCG(42, 42))
73+
pfxs := randomRealWorldPrefixes(prng, n)
74+
75+
orig := new(Table[*testVal])
76+
for _, pfx := range pfxs {
77+
orig.Insert(pfx, &testVal{Data: 1})
78+
}
79+
80+
var newVal *testVal
81+
clone := orig
82+
for _, pfx := range pfxs {
83+
clone, newVal = clone.UpdatePersist(pfx, func(val *testVal, ok bool) *testVal {
84+
if !ok {
85+
t.Fatalf("UpdatePersist: prefix %s not present", pfx)
86+
}
87+
return &testVal{Data: 2}
88+
})
89+
90+
// Mutate newVal to test for aliasing
91+
newVal.Data = 3
92+
93+
v1, _ := orig.Get(pfx)
94+
v2, _ := clone.Get(pfx)
95+
96+
if v1.Data != 1 {
97+
t.Errorf("UpdatePersist: original modified for %s: got=%q want=%q", pfx, v1.Data, 1)
98+
}
99+
100+
if v2.Data != 3 {
101+
t.Errorf("UpdatePersist: clone not correctly updated for %s: got=%q want=%q", pfx, v2.Data, 3)
102+
}
103+
104+
if v1 == v2 {
105+
t.Errorf("UpdatePersist: aliasing detected for %s", pfx)
106+
}
107+
}
108+
}
109+
110+
func TestDeletePersistTable(t *testing.T) {
111+
t.Parallel()
112+
113+
// setup
114+
const n = 10_000
115+
116+
prng := rand.New(rand.NewPCG(42, 42))
117+
pfxs := randomRealWorldPrefixes(prng, n)
118+
119+
orig := new(Table[*testVal])
120+
for _, pfx := range pfxs {
121+
orig.Insert(pfx, &testVal{Data: 1})
122+
}
123+
124+
clone := orig
125+
for _, pfx := range pfxs {
126+
clone = clone.DeletePersist(pfx)
127+
128+
// Deleted prefix should be absent in clone
129+
_, ok := clone.Get(pfx)
130+
if ok {
131+
t.Errorf("DeletePersist: prefix %s should've been deleted in clone, but it's still there", pfx)
132+
}
133+
134+
// Original table must be unchanged
135+
if v, ok := orig.Get(pfx); !ok || v.Data != 1 {
136+
t.Errorf("DeletePersist: original affected for %s", pfx)
137+
}
138+
}
139+
}
140+
141+
// ########## Lite ##############
142+
143+
func TestInsertPersistLite(t *testing.T) {
144+
t.Parallel()
145+
146+
// setup
147+
const n = 10_000
148+
prng := rand.New(rand.NewPCG(42, 42))
149+
pfxs := randomRealWorldPrefixes(prng, n)
150+
151+
orig := new(Lite)
152+
for _, pfx := range pfxs {
153+
orig.Insert(pfx)
154+
}
155+
156+
clone := orig
157+
for _, pfx := range pfxs {
158+
clone := clone.InsertPersist(pfx)
159+
160+
// both tables must have the pfx
161+
ok1 := orig.Exists(pfx)
162+
ok2 := clone.Exists(pfx)
163+
164+
if !ok1 {
165+
t.Errorf("InsertPersist: original table missing prefix %s", pfx)
166+
}
167+
168+
if !ok2 {
169+
t.Errorf("InsertPersist: cloned table missing prefix %s", pfx)
170+
}
171+
172+
size1 := orig.Size()
173+
size2 := clone.Size()
174+
175+
if size1 != n {
176+
t.Errorf("InsertPersist: original table has unexptected size, want %d, got %d", n, size1)
177+
}
178+
179+
if size2 != n {
180+
t.Errorf("InsertPersist: cloned table has unexptected size, want %d, got %d", n, size2)
181+
}
182+
}
183+
}
184+
185+
func TestDeletePersistLite(t *testing.T) {
186+
t.Parallel()
187+
188+
const n = 10_000
189+
190+
// setup
191+
prng := rand.New(rand.NewPCG(42, 42))
192+
pfxs := randomRealWorldPrefixes(prng, n)
193+
194+
orig := new(Lite)
195+
for _, pfx := range pfxs {
196+
orig.Insert(pfx)
197+
}
198+
199+
clone := orig
200+
for i, pfx := range pfxs {
201+
clone = clone.DeletePersist(pfx)
202+
203+
// test for existence
204+
if ok := orig.Exists(pfx); !ok {
205+
t.Errorf("DeletePersist: original table missing prefix %s", pfx)
206+
}
207+
208+
// test for absence
209+
if ok := clone.Exists(pfx); ok {
210+
t.Errorf("DeletePersist: prefix %s not deleted in cloned table", pfx)
211+
}
212+
213+
size1 := orig.Size()
214+
size2 := clone.Size()
215+
216+
if size1 != n {
217+
t.Errorf("InsertPersist: original table has unexptected size, want %d, got %d", n, size1)
218+
}
219+
220+
if size2 != n-i-1 {
221+
t.Errorf("InsertPersist: cloned table has unexptected size, want %d, got %d", n-i-1, size2)
222+
}
223+
}
224+
}

0 commit comments

Comments
 (0)