|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- // Copyright 2014 Google LLC
- // Modified 2018 by Jonathan Amsterdam (jbamsterdam@gmail.com)
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
-
- package btree
-
- import (
- "flag"
- "math/rand"
- "os"
- "sort"
- "sync"
- "testing"
- "time"
-
- "github.com/google/go-cmp/cmp"
- )
-
- func init() {
- seed := time.Now().Unix()
- rand.Seed(seed)
- }
-
- type itemWithIndex struct {
- Key Key
- Value Value
- Index int
- }
-
- // perm returns a random permutation of n Int items in the range [0, n).
- func perm(n int) []itemWithIndex {
- var out []itemWithIndex
- for _, v := range rand.Perm(n) {
- out = append(out, itemWithIndex{v, v, v})
- }
- return out
- }
-
- // rang returns an ordered list of Int items in the range [0, n).
- func rang(n int) []itemWithIndex {
- var out []itemWithIndex
- for i := 0; i < n; i++ {
- out = append(out, itemWithIndex{i, i, i})
- }
- return out
- }
-
- // all extracts all items from an iterator.
- func all(it *Iterator) []itemWithIndex {
- var out []itemWithIndex
- for it.Next() {
- out = append(out, itemWithIndex{it.Key, it.Value, it.Index})
- }
- return out
- }
-
- func reverse(s []itemWithIndex) {
- for i := 0; i < len(s)/2; i++ {
- s[i], s[len(s)-i-1] = s[len(s)-i-1], s[i]
- }
- }
-
- var btreeDegree = flag.Int("degree", 32, "B-Tree degree")
-
- func TestBTree(t *testing.T) {
- tr := New(*btreeDegree, less)
- const treeSize = 10000
- for i := 0; i < 10; i++ {
- if min, _ := tr.Min(); min != nil {
- t.Fatalf("empty min, got %+v", min)
- }
- if max, _ := tr.Max(); max != nil {
- t.Fatalf("empty max, got %+v", max)
- }
- for _, m := range perm(treeSize) {
- if _, ok := tr.Set(m.Key, m.Value); ok {
- t.Fatal("set found item", m)
- }
- }
- for _, m := range perm(treeSize) {
- _, ok, idx := tr.SetWithIndex(m.Key, m.Value)
- if !ok {
- t.Fatal("set didn't find item", m)
- }
- if idx != m.Index {
- t.Fatalf("got index %d, want %d", idx, m.Index)
- }
- }
- mink, minv := tr.Min()
- if want := 0; mink != want || minv != want {
- t.Fatalf("min: want %+v, got %+v, %+v", want, mink, minv)
- }
- maxk, maxv := tr.Max()
- if want := treeSize - 1; maxk != want || maxv != want {
- t.Fatalf("max: want %+v, got %+v, %+v", want, maxk, maxv)
- }
- got := all(tr.BeforeIndex(0))
- want := rang(treeSize)
- if !cmp.Equal(got, want) {
- t.Fatalf("mismatch:\n got: %v\nwant: %v", got, want)
- }
-
- for _, m := range perm(treeSize) {
- if _, removed := tr.Delete(m.Key); !removed {
- t.Fatalf("didn't find %v", m)
- }
- }
- if got = all(tr.BeforeIndex(0)); len(got) > 0 {
- t.Fatalf("some left!: %v", got)
- }
- }
- }
-
- func TestAt(t *testing.T) {
- tr := New(*btreeDegree, less)
- for _, m := range perm(100) {
- tr.Set(m.Key, m.Value)
- }
- for i := 0; i < tr.Len(); i++ {
- gotk, gotv := tr.At(i)
- if want := i; gotk != want || gotv != want {
- t.Fatalf("At(%d) = (%v, %v), want (%v, %v)", i, gotk, gotv, want, want)
- }
- }
- }
-
- func TestGetWithIndex(t *testing.T) {
- tr := New(*btreeDegree, less)
- for _, m := range perm(100) {
- tr.Set(m.Key, m.Value)
- }
- for i := 0; i < tr.Len(); i++ {
- gotv, goti := tr.GetWithIndex(i)
- wantv, wanti := i, i
- if gotv != wantv || goti != wanti {
- t.Errorf("GetWithIndex(%d) = (%v, %v), want (%v, %v)",
- i, gotv, goti, wantv, wanti)
- }
- }
- _, got := tr.GetWithIndex(100)
- if want := -1; got != want {
- t.Errorf("got %d, want %d", got, want)
- }
- }
-
- func TestSetWithIndex(t *testing.T) {
- tr := New(4, less) // use a small degree to cover more cases
- var contents []int
- for _, m := range perm(100) {
- _, _, idx := tr.SetWithIndex(m.Key, m.Value)
- contents = append(contents, m.Index)
- sort.Ints(contents)
- want := -1
- for i, c := range contents {
- if c == m.Index {
- want = i
- break
- }
- }
- if idx != want {
- t.Fatalf("got %d, want %d", idx, want)
- }
- }
- }
-
- func TestDeleteMin(t *testing.T) {
- tr := New(3, less)
- for _, m := range perm(100) {
- tr.Set(m.Key, m.Value)
- }
- var got []itemWithIndex
- for i := 0; tr.Len() > 0; i++ {
- k, v := tr.DeleteMin()
- got = append(got, itemWithIndex{k, v, i})
- }
- if want := rang(100); !cmp.Equal(got, want) {
- t.Fatalf("got: %v\nwant: %v", got, want)
- }
- }
-
- func TestDeleteMax(t *testing.T) {
- tr := New(3, less)
- for _, m := range perm(100) {
- tr.Set(m.Key, m.Value)
- }
- var got []itemWithIndex
- for tr.Len() > 0 {
- k, v := tr.DeleteMax()
- got = append(got, itemWithIndex{k, v, tr.Len()})
- }
- reverse(got)
- if want := rang(100); !cmp.Equal(got, want) {
- t.Fatalf("got: %v\nwant: %v", got, want)
- }
- }
-
- func TestIterator(t *testing.T) {
- const size = 10
-
- tr := New(2, less)
- // Empty tree.
- for i, it := range []*Iterator{
- tr.BeforeIndex(0),
- tr.Before(3),
- tr.After(3),
- } {
- if got, want := it.Next(), false; got != want {
- t.Errorf("empty, #%d: got %t, want %t", i, got, want)
- }
- }
-
- // Root with zero children.
- tr.Set(1, nil)
- tr.Delete(1)
- if !(tr.root != nil && len(tr.root.children) == 0 && len(tr.root.items) == 0) {
- t.Fatal("wrong shape tree")
- }
- for i, it := range []*Iterator{
- tr.BeforeIndex(0),
- tr.Before(3),
- tr.After(3),
- } {
- if got, want := it.Next(), false; got != want {
- t.Errorf("zero root, #%d: got %t, want %t", i, got, want)
- }
- }
-
- // Tree with size elements.
- p := perm(size)
- for _, v := range p {
- tr.Set(v.Key, v.Value)
- }
-
- it := tr.BeforeIndex(0)
- got := all(it)
- want := rang(size)
- if !cmp.Equal(got, want) {
- t.Fatalf("got %+v\nwant %+v\n", got, want)
- }
-
- for i, w := range want {
- it := tr.Before(w.Key)
- got = all(it)
- wn := want[w.Key.(int):]
- if !cmp.Equal(got, wn) {
- t.Fatalf("got %+v\nwant %+v\n", got, wn)
- }
-
- it = tr.BeforeIndex(i)
- got = all(it)
- if !cmp.Equal(got, wn) {
- t.Fatalf("got %+v\nwant %+v\n", got, wn)
- }
-
- it = tr.After(w.Key)
- got = all(it)
- wn = append([]itemWithIndex(nil), want[:w.Key.(int)+1]...)
- reverse(wn)
- if !cmp.Equal(got, wn) {
- t.Fatalf("got %+v\nwant %+v\n", got, wn)
- }
-
- it = tr.AfterIndex(i)
- got = all(it)
- if !cmp.Equal(got, wn) {
- t.Fatalf("got %+v\nwant %+v\n", got, wn)
- }
- }
-
- // Non-existent keys.
- tr = New(2, less)
- for _, v := range p {
- tr.Set(v.Key.(int)*2, v.Value)
- }
- // tr has only even keys: 0, 2, 4, ... Iterate from odd keys.
- for i := -1; i <= size+1; i += 2 {
- it := tr.Before(i)
- got := all(it)
- var want []itemWithIndex
- for j := (i + 1) / 2; j < size; j++ {
- want = append(want, itemWithIndex{j * 2, j, j})
- }
- if !cmp.Equal(got, want) {
- tr.print(os.Stdout)
- t.Fatalf("%d: got %+v\nwant %+v\n", i, got, want)
- }
-
- it = tr.After(i)
- got = all(it)
- want = nil
- for j := (i - 1) / 2; j >= 0; j-- {
- want = append(want, itemWithIndex{j * 2, j, j})
- }
- if !cmp.Equal(got, want) {
- t.Fatalf("%d: got %+v\nwant %+v\n", i, got, want)
- }
- }
- }
-
- func TestMixed(t *testing.T) {
- // Test random, mixed insertions and deletions.
- const maxSize = 1000
- tr := New(3, less)
- has := map[int]bool{}
- for i := 0; i < 10000; i++ {
- r := rand.Intn(maxSize)
- if r >= tr.Len() {
- old, ok := tr.Set(r, r)
- if has[r] != ok {
- t.Fatalf("%d: has=%t, ok=%t", r, has[r], ok)
- }
- if ok && old.(int) != r {
- t.Fatalf("%d: bad old", r)
- }
- has[r] = true
- if got, want := tr.Get(r), r; got != want {
- t.Fatalf("Get(%d) = %d, want %d", r, got, want)
- }
- } else {
- // Expoit random map iteration order.
- var d int
- for d = range has {
- break
- }
- old, removed := tr.Delete(d)
- if !removed {
- t.Fatalf("%d not found", d)
- }
- if old.(int) != d {
- t.Fatalf("%d: bad old", d)
- }
- delete(has, d)
- }
- }
- }
-
- const cloneTestSize = 10000
-
- func cloneTest(t *testing.T, b *BTree, start int, p []itemWithIndex, wg *sync.WaitGroup, treec chan<- *BTree) {
- treec <- b
- for i := start; i < cloneTestSize; i++ {
- b.Set(p[i].Key, p[i].Value)
- if i%(cloneTestSize/5) == 0 {
- wg.Add(1)
- go cloneTest(t, b.Clone(), i+1, p, wg, treec)
- }
- }
- wg.Done()
- }
-
- func TestCloneConcurrentOperations(t *testing.T) {
- b := New(*btreeDegree, less)
- treec := make(chan *BTree)
- p := perm(cloneTestSize)
- var wg sync.WaitGroup
- wg.Add(1)
- go cloneTest(t, b, 0, p, &wg, treec)
- var trees []*BTree
- donec := make(chan struct{})
- go func() {
- for t := range treec {
- trees = append(trees, t)
- }
- close(donec)
- }()
- wg.Wait()
- close(treec)
- <-donec
- want := rang(cloneTestSize)
- for i, tree := range trees {
- if !cmp.Equal(want, all(tree.BeforeIndex(0))) {
- t.Errorf("tree %v mismatch", i)
- }
- }
- toRemove := rang(cloneTestSize)[cloneTestSize/2:]
- for i := 0; i < len(trees)/2; i++ {
- tree := trees[i]
- wg.Add(1)
- go func() {
- for _, m := range toRemove {
- tree.Delete(m.Key)
- }
- wg.Done()
- }()
- }
- wg.Wait()
- for i, tree := range trees {
- var wantpart []itemWithIndex
- if i < len(trees)/2 {
- wantpart = want[:cloneTestSize/2]
- } else {
- wantpart = want
- }
- if got := all(tree.BeforeIndex(0)); !cmp.Equal(wantpart, got) {
- t.Errorf("tree %v mismatch, want %v got %v", i, len(want), len(got))
- }
- }
- }
-
- func less(a, b interface{}) bool { return a.(int) < b.(int) }
|