This repository has been archived on 2022-07-30. You can view files and clone it, but cannot push or open issues/pull-requests.
mdb/btreeindex_test.go

377 lines
8.0 KiB
Go

package mdb
/*
func TestBTreeIndex(t *testing.T) {
type Item struct {
ID uint64
Name string
}
checkIndexOne := func(idx *BTreeIndex[Item], expected ...Item) error {
if idx.Len() != len(expected) {
return fmt.Errorf("Expected %d items but found %d.", len(expected), idx.Len())
}
if len(expected) == 0 {
return nil
}
for _, item := range expected {
item2, ok := idx.Get(item)
if !ok {
return fmt.Errorf("Missing expected item: %v", item)
}
if !reflect.DeepEqual(item, item2) {
return fmt.Errorf("Items not equal: %v != %v", item2, item)
}
}
item, ok := idx.Min()
if !ok {
return fmt.Errorf("Min item not found, expected: %v", expected[0])
}
if !reflect.DeepEqual(item, expected[0]) {
return fmt.Errorf("Min items not equal: %v != %v", item, expected[0])
}
item, ok = idx.Max()
i := len(expected) - 1
if !ok {
return fmt.Errorf("Max item not found, expected: %v", expected[i])
}
if !reflect.DeepEqual(item, expected[i]) {
return fmt.Errorf("Max items not equal: %v != %v", item, expected[i])
}
i = 0
iter := idx.Ascend()
defer iter.Close()
for iter.Next() {
if !reflect.DeepEqual(iter.Value(), expected[i]) {
return fmt.Errorf("Items not equal (%d): %v != %v", i, iter.Value(), expected[i])
}
i++
}
i = len(expected) - 1
iter = idx.Descend()
defer iter.Close()
for iter.Next() {
if !reflect.DeepEqual(iter.Value(), expected[i]) {
return fmt.Errorf("Items not equal (%d): %v != %v", i, iter.Value(), expected[i])
}
i--
}
i = 1
iter = idx.AscendAfter(expected[1])
defer iter.Close()
for iter.Next() {
if !reflect.DeepEqual(iter.Value(), expected[i]) {
return fmt.Errorf("Items not equal (%d): %v != %v", i, iter.Value(), expected[i])
}
i++
}
i = len(expected) - 2
iter = idx.DescendAfter(expected[len(expected)-2])
defer iter.Close()
for iter.Next() {
if !reflect.DeepEqual(iter.Value(), expected[i]) {
return fmt.Errorf("Items not equal (%d): %v != %v", i, iter.Value(), expected[i])
}
i--
}
return nil
}
checkIndex := func(idx *BTreeIndex[Item], expected ...Item) error {
idx.c.db.waitForWAL()
if err := checkIndexOne(idx, expected...); err != nil {
return fmt.Errorf("%w: original", err)
}
db := NewPrimary(idx.c.db.root)
defer db.Close()
c := NewCollection(db, "collection", func(i *Item) uint64 { return i.ID })
idx = NewBTreeIndex(c,
func(i, j *Item) bool { return i.Name < j.Name },
func(i *Item) bool { return i.Name != "" })
db.Start()
return checkIndexOne(idx, expected...)
}
run := func(name string, inner func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item])) {
t.Run(name, func(t *testing.T) {
root := filepath.Join(os.TempDir(), randString())
//defer os.RemoveAll(root)
db := NewPrimary(root)
defer db.Close()
c := NewCollection(db, "collection", func(i *Item) uint64 { return i.ID })
idx := NewBTreeIndex(c,
func(i, j *Item) bool { return i.Name < j.Name },
func(i *Item) bool { return i.Name != "" })
db.Start()
inner(t, c, idx)
})
}
run("no items", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
if err := checkIndex(idx); err != nil {
t.Fatal(err)
}
})
run("insert some", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
item1 := Item{1, "one"}
item2 := Item{2, ""}
item3 := Item{3, "three"}
item4 := Item{4, ""}
item5 := Item{5, "five"}
c.Insert(item1)
c.Insert(item2)
c.Insert(item3)
c.Insert(item4)
c.Insert(item5)
if err := checkIndex(idx, item5, item1, item3); err != nil {
t.Fatal(err)
}
})
run("partial iteration", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
item1 := Item{1, "one"}
item2 := Item{2, ""}
item3 := Item{3, "three"}
item4 := Item{4, ""}
item5 := Item{5, "five"}
c.Insert(item1)
c.Insert(item2)
c.Insert(item3)
c.Insert(item4)
c.Insert(item5)
iter := idx.Ascend()
defer iter.Close()
if !iter.Next() {
t.Fatal("Expected", item5)
}
if !reflect.DeepEqual(iter.Value(), item5) {
t.Fatal(iter.Value(), item5)
}
})
run("get", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
item1 := Item{1, "one"}
item2 := Item{2, ""}
c.Insert(item1)
c.Insert(item2)
item, ok := idx.Get(Item{0, "one"})
if !ok || !reflect.DeepEqual(item, item1) {
t.Fatal(ok, item, item1)
}
})
run("get not found", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
item1 := Item{1, "one"}
item2 := Item{2, ""}
c.Insert(item1)
c.Insert(item2)
if item, ok := idx.Get(Item{0, "three"}); ok {
t.Fatal(item)
}
})
run("min max on empty", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
item2 := Item{2, ""}
c.Insert(item2)
if item, ok := idx.Min(); ok {
t.Fatal(item)
}
if item, ok := idx.Max(); ok {
t.Fatal(item)
}
})
run("min max with one item", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
item1 := Item{1, "one"}
item2 := Item{2, ""}
c.Insert(item1)
c.Insert(item2)
i1, ok := idx.Min()
if !ok {
t.Fatal(ok)
}
i2, ok := idx.Max()
if !ok {
t.Fatal(ok)
}
if !reflect.DeepEqual(i1, i2) {
t.Fatal(i1, i2)
}
})
run("update outside of index", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
item1 := Item{1, "one"}
item2 := Item{2, ""}
item3 := Item{3, "three"}
item4 := Item{4, ""}
item5 := Item{5, "five"}
c.Insert(item1)
c.Insert(item2)
c.Insert(item3)
c.Insert(item4)
c.Insert(item5)
c.Update(2, func(in Item) (Item, error) {
return in, nil
})
if err := checkIndex(idx, item5, item1, item3); err != nil {
t.Fatal(err)
}
})
run("update into index", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
item1 := Item{1, "one"}
item2 := Item{2, ""}
item3 := Item{3, "three"}
item4 := Item{4, ""}
item5 := Item{5, "five"}
c.Insert(item1)
c.Insert(item2)
c.Insert(item3)
c.Insert(item4)
c.Insert(item5)
err := c.Update(2, func(in Item) (Item, error) {
in.Name = "two"
return in, nil
})
if err != nil {
t.Fatal(err)
}
item2.Name = "two"
if err := checkIndex(idx, item5, item1, item3, item2); err != nil {
t.Fatal(err)
}
})
run("update out of index", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
item1 := Item{1, "one"}
item2 := Item{2, ""}
item3 := Item{3, "three"}
item4 := Item{4, ""}
item5 := Item{5, "five"}
c.Insert(item1)
c.Insert(item2)
c.Insert(item3)
c.Insert(item4)
c.Insert(item5)
err := c.Update(1, func(in Item) (Item, error) {
in.Name = ""
return in, nil
})
if err != nil {
t.Fatal(err)
}
if err := checkIndex(idx, item5, item3); err != nil {
t.Fatal(err)
}
})
run("update within index", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
item1 := Item{1, "one"}
item2 := Item{2, ""}
item3 := Item{3, "three"}
item4 := Item{4, ""}
item5 := Item{5, "five"}
c.Insert(item1)
c.Insert(item2)
c.Insert(item3)
c.Insert(item4)
c.Insert(item5)
err := c.Update(1, func(in Item) (Item, error) {
in.Name = "xone"
return in, nil
})
if err != nil {
t.Fatal(err)
}
item1.Name = "xone"
if err := checkIndex(idx, item5, item3, item1); err != nil {
t.Fatal(err)
}
})
run("delete outside index", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
item1 := Item{1, "one"}
item2 := Item{2, ""}
item3 := Item{3, "three"}
item4 := Item{4, ""}
item5 := Item{5, "five"}
c.Insert(item1)
c.Insert(item2)
c.Insert(item3)
c.Insert(item4)
c.Insert(item5)
c.Delete(item2.ID)
if err := checkIndex(idx, item5, item1, item3); err != nil {
t.Fatal(err)
}
})
run("delete within index", func(t *testing.T, c *Collection[Item], idx *BTreeIndex[Item]) {
item1 := Item{1, "one"}
item2 := Item{2, ""}
item3 := Item{3, "three"}
item4 := Item{4, ""}
item5 := Item{5, "five"}
c.Insert(item1)
c.Insert(item2)
c.Insert(item3)
c.Insert(item4)
c.Insert(item5)
c.Delete(item1.ID)
if err := checkIndex(idx, item5, item3); err != nil {
t.Fatal(err)
}
})
}
*/