1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package btree
17
18 import (
19 "flag"
20 "math/rand"
21 "os"
22 "sort"
23 "sync"
24 "testing"
25 "time"
26
27 "github.com/google/go-cmp/cmp"
28 )
29
30 func init() {
31 seed := time.Now().Unix()
32 rand.Seed(seed)
33 }
34
35 type itemWithIndex struct {
36 Key Key
37 Value Value
38 Index int
39 }
40
41
42 func perm(n int) []itemWithIndex {
43 var out []itemWithIndex
44 for _, v := range rand.Perm(n) {
45 out = append(out, itemWithIndex{v, v, v})
46 }
47 return out
48 }
49
50
51 func rang(n int) []itemWithIndex {
52 var out []itemWithIndex
53 for i := 0; i < n; i++ {
54 out = append(out, itemWithIndex{i, i, i})
55 }
56 return out
57 }
58
59
60 func all(it *Iterator) []itemWithIndex {
61 var out []itemWithIndex
62 for it.Next() {
63 out = append(out, itemWithIndex{it.Key, it.Value, it.Index})
64 }
65 return out
66 }
67
68 func reverse(s []itemWithIndex) {
69 for i := 0; i < len(s)/2; i++ {
70 s[i], s[len(s)-i-1] = s[len(s)-i-1], s[i]
71 }
72 }
73
74 var btreeDegree = flag.Int("degree", 32, "B-Tree degree")
75
76 func TestBTree(t *testing.T) {
77 tr := New(*btreeDegree, less)
78 const treeSize = 10000
79 for i := 0; i < 10; i++ {
80 if min, _ := tr.Min(); min != nil {
81 t.Fatalf("empty min, got %+v", min)
82 }
83 if max, _ := tr.Max(); max != nil {
84 t.Fatalf("empty max, got %+v", max)
85 }
86 for _, m := range perm(treeSize) {
87 if _, ok := tr.Set(m.Key, m.Value); ok {
88 t.Fatal("set found item", m)
89 }
90 }
91 for _, m := range perm(treeSize) {
92 _, ok, idx := tr.SetWithIndex(m.Key, m.Value)
93 if !ok {
94 t.Fatal("set didn't find item", m)
95 }
96 if idx != m.Index {
97 t.Fatalf("got index %d, want %d", idx, m.Index)
98 }
99 }
100 mink, minv := tr.Min()
101 if want := 0; mink != want || minv != want {
102 t.Fatalf("min: want %+v, got %+v, %+v", want, mink, minv)
103 }
104 maxk, maxv := tr.Max()
105 if want := treeSize - 1; maxk != want || maxv != want {
106 t.Fatalf("max: want %+v, got %+v, %+v", want, maxk, maxv)
107 }
108 got := all(tr.BeforeIndex(0))
109 want := rang(treeSize)
110 if !cmp.Equal(got, want) {
111 t.Fatalf("mismatch:\n got: %v\nwant: %v", got, want)
112 }
113
114 for _, m := range perm(treeSize) {
115 if _, removed := tr.Delete(m.Key); !removed {
116 t.Fatalf("didn't find %v", m)
117 }
118 }
119 if got = all(tr.BeforeIndex(0)); len(got) > 0 {
120 t.Fatalf("some left!: %v", got)
121 }
122 }
123 }
124
125 func TestAt(t *testing.T) {
126 tr := New(*btreeDegree, less)
127 for _, m := range perm(100) {
128 tr.Set(m.Key, m.Value)
129 }
130 for i := 0; i < tr.Len(); i++ {
131 gotk, gotv := tr.At(i)
132 if want := i; gotk != want || gotv != want {
133 t.Fatalf("At(%d) = (%v, %v), want (%v, %v)", i, gotk, gotv, want, want)
134 }
135 }
136 }
137
138 func TestGetWithIndex(t *testing.T) {
139 tr := New(*btreeDegree, less)
140 for _, m := range perm(100) {
141 tr.Set(m.Key, m.Value)
142 }
143 for i := 0; i < tr.Len(); i++ {
144 gotv, goti := tr.GetWithIndex(i)
145 wantv, wanti := i, i
146 if gotv != wantv || goti != wanti {
147 t.Errorf("GetWithIndex(%d) = (%v, %v), want (%v, %v)",
148 i, gotv, goti, wantv, wanti)
149 }
150 }
151 _, got := tr.GetWithIndex(100)
152 if want := -1; got != want {
153 t.Errorf("got %d, want %d", got, want)
154 }
155 }
156
157 func TestSetWithIndex(t *testing.T) {
158 tr := New(4, less)
159 var contents []int
160 for _, m := range perm(100) {
161 _, _, idx := tr.SetWithIndex(m.Key, m.Value)
162 contents = append(contents, m.Index)
163 sort.Ints(contents)
164 want := -1
165 for i, c := range contents {
166 if c == m.Index {
167 want = i
168 break
169 }
170 }
171 if idx != want {
172 t.Fatalf("got %d, want %d", idx, want)
173 }
174 }
175 }
176
177 func TestDeleteMin(t *testing.T) {
178 tr := New(3, less)
179 for _, m := range perm(100) {
180 tr.Set(m.Key, m.Value)
181 }
182 var got []itemWithIndex
183 for i := 0; tr.Len() > 0; i++ {
184 k, v := tr.DeleteMin()
185 got = append(got, itemWithIndex{k, v, i})
186 }
187 if want := rang(100); !cmp.Equal(got, want) {
188 t.Fatalf("got: %v\nwant: %v", got, want)
189 }
190 }
191
192 func TestDeleteMax(t *testing.T) {
193 tr := New(3, less)
194 for _, m := range perm(100) {
195 tr.Set(m.Key, m.Value)
196 }
197 var got []itemWithIndex
198 for tr.Len() > 0 {
199 k, v := tr.DeleteMax()
200 got = append(got, itemWithIndex{k, v, tr.Len()})
201 }
202 reverse(got)
203 if want := rang(100); !cmp.Equal(got, want) {
204 t.Fatalf("got: %v\nwant: %v", got, want)
205 }
206 }
207
208 func TestIterator(t *testing.T) {
209 const size = 10
210
211 tr := New(2, less)
212
213 for i, it := range []*Iterator{
214 tr.BeforeIndex(0),
215 tr.Before(3),
216 tr.After(3),
217 } {
218 if got, want := it.Next(), false; got != want {
219 t.Errorf("empty, #%d: got %t, want %t", i, got, want)
220 }
221 }
222
223
224 tr.Set(1, nil)
225 tr.Delete(1)
226 if !(tr.root != nil && len(tr.root.children) == 0 && len(tr.root.items) == 0) {
227 t.Fatal("wrong shape tree")
228 }
229 for i, it := range []*Iterator{
230 tr.BeforeIndex(0),
231 tr.Before(3),
232 tr.After(3),
233 } {
234 if got, want := it.Next(), false; got != want {
235 t.Errorf("zero root, #%d: got %t, want %t", i, got, want)
236 }
237 }
238
239
240 p := perm(size)
241 for _, v := range p {
242 tr.Set(v.Key, v.Value)
243 }
244
245 it := tr.BeforeIndex(0)
246 got := all(it)
247 want := rang(size)
248 if !cmp.Equal(got, want) {
249 t.Fatalf("got %+v\nwant %+v\n", got, want)
250 }
251
252 for i, w := range want {
253 it := tr.Before(w.Key)
254 got = all(it)
255 wn := want[w.Key.(int):]
256 if !cmp.Equal(got, wn) {
257 t.Fatalf("got %+v\nwant %+v\n", got, wn)
258 }
259
260 it = tr.BeforeIndex(i)
261 got = all(it)
262 if !cmp.Equal(got, wn) {
263 t.Fatalf("got %+v\nwant %+v\n", got, wn)
264 }
265
266 it = tr.After(w.Key)
267 got = all(it)
268 wn = append([]itemWithIndex(nil), want[:w.Key.(int)+1]...)
269 reverse(wn)
270 if !cmp.Equal(got, wn) {
271 t.Fatalf("got %+v\nwant %+v\n", got, wn)
272 }
273
274 it = tr.AfterIndex(i)
275 got = all(it)
276 if !cmp.Equal(got, wn) {
277 t.Fatalf("got %+v\nwant %+v\n", got, wn)
278 }
279 }
280
281
282 tr = New(2, less)
283 for _, v := range p {
284 tr.Set(v.Key.(int)*2, v.Value)
285 }
286
287 for i := -1; i <= size+1; i += 2 {
288 it := tr.Before(i)
289 got := all(it)
290 var want []itemWithIndex
291 for j := (i + 1) / 2; j < size; j++ {
292 want = append(want, itemWithIndex{j * 2, j, j})
293 }
294 if !cmp.Equal(got, want) {
295 tr.print(os.Stdout)
296 t.Fatalf("%d: got %+v\nwant %+v\n", i, got, want)
297 }
298
299 it = tr.After(i)
300 got = all(it)
301 want = nil
302 for j := (i - 1) / 2; j >= 0; j-- {
303 want = append(want, itemWithIndex{j * 2, j, j})
304 }
305 if !cmp.Equal(got, want) {
306 t.Fatalf("%d: got %+v\nwant %+v\n", i, got, want)
307 }
308 }
309 }
310
311 func TestMixed(t *testing.T) {
312
313 const maxSize = 1000
314 tr := New(3, less)
315 has := map[int]bool{}
316 for i := 0; i < 10000; i++ {
317 r := rand.Intn(maxSize)
318 if r >= tr.Len() {
319 old, ok := tr.Set(r, r)
320 if has[r] != ok {
321 t.Fatalf("%d: has=%t, ok=%t", r, has[r], ok)
322 }
323 if ok && old.(int) != r {
324 t.Fatalf("%d: bad old", r)
325 }
326 has[r] = true
327 if got, want := tr.Get(r), r; got != want {
328 t.Fatalf("Get(%d) = %d, want %d", r, got, want)
329 }
330 } else {
331
332 var d int
333 for d = range has {
334 break
335 }
336 old, removed := tr.Delete(d)
337 if !removed {
338 t.Fatalf("%d not found", d)
339 }
340 if old.(int) != d {
341 t.Fatalf("%d: bad old", d)
342 }
343 delete(has, d)
344 }
345 }
346 }
347
348 const cloneTestSize = 10000
349
350 func cloneTest(t *testing.T, b *BTree, start int, p []itemWithIndex, wg *sync.WaitGroup, treec chan<- *BTree) {
351 treec <- b
352 for i := start; i < cloneTestSize; i++ {
353 b.Set(p[i].Key, p[i].Value)
354 if i%(cloneTestSize/5) == 0 {
355 wg.Add(1)
356 go cloneTest(t, b.Clone(), i+1, p, wg, treec)
357 }
358 }
359 wg.Done()
360 }
361
362 func TestCloneConcurrentOperations(t *testing.T) {
363 b := New(*btreeDegree, less)
364 treec := make(chan *BTree)
365 p := perm(cloneTestSize)
366 var wg sync.WaitGroup
367 wg.Add(1)
368 go cloneTest(t, b, 0, p, &wg, treec)
369 var trees []*BTree
370 donec := make(chan struct{})
371 go func() {
372 for t := range treec {
373 trees = append(trees, t)
374 }
375 close(donec)
376 }()
377 wg.Wait()
378 close(treec)
379 <-donec
380 want := rang(cloneTestSize)
381 for i, tree := range trees {
382 if !cmp.Equal(want, all(tree.BeforeIndex(0))) {
383 t.Errorf("tree %v mismatch", i)
384 }
385 }
386 toRemove := rang(cloneTestSize)[cloneTestSize/2:]
387 for i := 0; i < len(trees)/2; i++ {
388 tree := trees[i]
389 wg.Add(1)
390 go func() {
391 for _, m := range toRemove {
392 tree.Delete(m.Key)
393 }
394 wg.Done()
395 }()
396 }
397 wg.Wait()
398 for i, tree := range trees {
399 var wantpart []itemWithIndex
400 if i < len(trees)/2 {
401 wantpart = want[:cloneTestSize/2]
402 } else {
403 wantpart = want
404 }
405 if got := all(tree.BeforeIndex(0)); !cmp.Equal(wantpart, got) {
406 t.Errorf("tree %v mismatch, want %v got %v", i, len(want), len(got))
407 }
408 }
409 }
410
411 func less(a, b interface{}) bool { return a.(int) < b.(int) }
412
View as plain text