blob: 8784c6bfb4a5e8ee72d55f48778e15033d311826 [file] [log] [blame]
// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package storage
import (
"bytes"
"slices"
"testing"
"rsc.io/ordered"
)
func TestOverlayDB(t *testing.T) {
o := func(n int) []byte { return ordered.Encode(n) }
newbase := func() DB {
db := MemDB()
db.Set(o(0), o(0))
db.Set(o(9), o(9))
return db
}
for _, test := range []struct {
name string
ops func(DB)
}{
{
"set3",
func(db DB) { db.Set(o(3), o(3)) },
},
{
"del9",
func(db DB) { db.Delete(o(9)) },
},
{
"del9set9",
func(db DB) { db.Delete(o(9)); db.Set(o(9), o(4)) },
},
{
"del5set3",
func(db DB) { db.Delete(o(5)); db.Set(o(3), o(3)) },
},
{
"get9",
func(db DB) {
v, ok := db.Get(o(9))
if !ok || !bytes.Equal(v, o(9)) {
t.Fatal("bad Get")
}
},
},
{
"set9get9",
func(db DB) {
db.Set(o(9), o(1))
v, ok := db.Get(o(9))
if !ok || !bytes.Equal(v, o(1)) {
t.Fatal("bad Get")
}
},
},
{
"del9get9",
func(db DB) {
db.Delete(o(9))
if _, ok := db.Get(o(9)); ok {
t.Fatal("bad Get")
}
},
},
{
"batch",
func(db DB) {
b := db.Batch()
b.Set(o(1), o(1))
b.Set(o(2), o(2))
b.Delete(o(9))
b.Set(o(9), o(9))
b.DeleteRange(o(2), o(6))
b.Apply()
},
},
{
"delrange",
func(db DB) {
for i := range 9 {
db.Set(o(i), o(i))
}
db.DeleteRange(o(3), o(9))
db.Set(o(3), o(3))
},
},
{
"delrange2",
func(db DB) {
for i := range 20 {
db.Set(o(i), o(i))
}
db.DeleteRange(o(3), o(9))
db.DeleteRange(o(8), o(12))
db.DeleteRange(o(18), o(22))
for _, k := range []int{4, 12, 15, 18, 23} {
db.Set(o(k), o(k))
}
db.Delete(o(4))
db.Delete(o(15))
},
},
} {
t.Run(test.name, func(t *testing.T) {
// Run the operations on an overlay DB.
base := newbase()
over := MemDB()
gdb := NewOverlayDB(over, base)
test.ops(gdb)
// Run the operations directly on the base DB.
wdb := newbase()
test.ops(wdb)
// The overlay DB should behave exactly like an ordinary DB.
got := items(gdb)
want := items(wdb)
if !slices.EqualFunc(got, want, item.Equal) {
t.Errorf("\ngot %v\nwant %v", got, want)
}
// The overlay DB should not change its base.
bgot := items(base)
if !slices.EqualFunc(bgot, items(newbase()), item.Equal) {
t.Errorf("base changed: %v", bgot)
}
})
}
}
type item struct {
key, val []byte
}
func (i1 item) Equal(i2 item) bool {
return bytes.Equal(i1.key, i2.key) && bytes.Equal(i1.val, i2.val)
}
func items(db DB) []item {
var items []item
for k, vf := range db.Scan(nil, ordered.Encode(ordered.Inf)) {
var prefix string
if _, err := ordered.DecodePrefix(k, &prefix); err == nil && prefix == overlayPrefix {
continue
}
items = append(items, item{k, vf()})
}
return items
}