2021-12-04 16:42:11 +00:00

295 lines
7.5 KiB
Go

package graviton
import (
"encoding/base64"
"math/rand"
"reflect"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func randString(len int) string {
buff := make([]byte, len)
rand.Read(buff)
return base64.StdEncoding.EncodeToString(buff)
}
// this test various diffing related code
func TestDiffTree(t *testing.T) {
store, err := NewMemStore()
require.NoError(t, err)
gv, err := store.LoadSnapshot(0)
require.NoError(t, err)
tree, err := gv.GetTree("root")
require.NoError(t, err)
keyval_reference := map[string]string{}
var keys []string
rand.Seed(time.Now().UnixNano())
for i := 0; i < 100000; i++ {
key := randString(32)
value := randString(8)
keyval_reference[key] = value
keys = append(keys, key)
tree.Put([]byte(key), []byte(value))
}
tree.Commit() // commit the tree
insert_map_reference := map[string]string{}
delete_map_reference := map[string]string{}
modify_map_reference := map[string]string{}
insert_map_actual := map[string]string{}
delete_map_actual := map[string]string{}
modify_map_actual := map[string]string{}
for i := 0; i < len(keyval_reference)/3; i++ {
key := randString(32)
value := randString(8)
_ = key
_ = value
insert_map_reference[key] = value
tree.Put([]byte(key), []byte(value))
}
for k := range keyval_reference {
if len(modify_map_reference) < len(keyval_reference)/3 {
value := randString(8)
modify_map_reference[k] = value
tree.Put([]byte(k), []byte(value))
} else {
break
}
}
// delete should be after modify
for len(delete_map_reference) < len(insert_map_reference) {
rkey := keys[rand.Intn(len(keys))]
if _, ok := modify_map_reference[rkey]; !ok {
delete_map_reference[rkey] = keyval_reference[rkey]
tree.Delete([]byte(rkey))
}
}
tree.Commit() // commit the tree
gv1, _ := store.LoadSnapshot(1)
base_tree, _ := gv1.GetTree("root")
gv2, _ := store.LoadSnapshot(2)
head_tree, _ := gv2.GetTree("root")
insert_handler := func(k, v []byte) {
insert_map_actual[string(k)] = string(v)
}
delete_handler := func(k, v []byte) {
delete_map_actual[string(k)] = string(v)
}
modify_handler := func(k, v []byte) {
//t.Logf("k %s v %s reference %s" , string(k), string(v) , modify_map_reference[string(k)])
modify_map_actual[string(k)] = string(v)
}
err = Diff(base_tree, head_tree, delete_handler, modify_handler, insert_handler)
//t.Logf("err %s",err)
//t.Logf("delete %d modify %d insert %d", len(delete_map_actual), len(modify_map_actual), len(insert_map_actual) )
if !reflect.DeepEqual(insert_map_reference, insert_map_actual) {
t.Logf(" insert map reference %d actual %d", len(insert_map_reference), len(insert_map_actual))
t.Fatalf("inserts could not be diffed")
}
if !reflect.DeepEqual(delete_map_reference, delete_map_actual) {
t.Logf(" delete map count %d", len(delete_map_actual))
t.Fatalf("delete could not be diffed")
}
if !reflect.DeepEqual(modify_map_reference, modify_map_actual) {
t.Logf(" modify_handler reference %d actual %d", len(modify_map_reference), len(modify_map_actual))
t.Logf("refer %+v", modify_map_reference)
t.Logf("refer %+v", modify_map_actual)
t.Fatalf("modify could not be diffed")
}
// now we must swap the trees
insert_map_actual = map[string]string{}
delete_map_actual = map[string]string{}
modify_map_actual = map[string]string{}
insert_map_reference, delete_map_reference = delete_map_reference, insert_map_reference
for k, _ := range modify_map_reference {
modify_map_reference[k] = keyval_reference[k]
}
base_tree, head_tree = head_tree, base_tree // swap the actual tree
err = Diff(base_tree, head_tree, delete_handler, modify_handler, insert_handler)
//t.Logf("err %s",err)
//t.Logf("delete %d modify %d insert %d", len(delete_map_actual), len(modify_map_actual), len(insert_map_actual) )
if !reflect.DeepEqual(insert_map_reference, insert_map_actual) {
t.Logf(" insert map reference %d actual %d", len(insert_map_reference), len(insert_map_actual))
t.Fatalf("inserts could not be diffed")
}
if !reflect.DeepEqual(delete_map_reference, delete_map_actual) {
t.Logf(" delete map count %d", len(delete_map_actual))
t.Fatalf("delete could not be diffed")
}
if !reflect.DeepEqual(modify_map_reference, modify_map_actual) {
t.Logf(" modify_handler map count %d ", len(modify_map_actual))
t.Fatalf("modify could not be diffed")
}
}
// this test various cursor diff related code
func TestDiffTree_Single_Key(t *testing.T) {
store, err := NewMemStore()
require.NoError(t, err)
gv, err := store.LoadSnapshot(0)
require.NoError(t, err)
tree, err := gv.GetTree("root")
require.NoError(t, err)
rand.Seed(time.Now().UnixNano())
key := randString(32)
value := randString(8)
mod_value := randString(8)
tree.Put([]byte(key), []byte(value))
tree.Commit() // commit the tree
//tree.Hash()
//tree.graph("/tmp/1.dot")
tree.Put([]byte(key), []byte(mod_value))
tree.Commit() // commit the tree
// tree.graph("/tmp/2.dot")
gv1, _ := store.LoadSnapshot(1)
base_tree, _ := gv1.GetTree("root")
//base_tree.graph("/tmp/11.dot")
gv2, _ := store.LoadSnapshot(2)
head_tree, _ := gv2.GetTree("root")
// head_tree.graph("/tmp/12.dot")
insert_handler := func(k, v []byte) {
panic("insert")
}
delete_handler := func(k, v []byte) {
panic("insert")
}
mod_count := 0
modify_handler := func(k, v []byte) {
mod_count++
if mod_count > 1 {
panic("more modified")
}
if key != string(k) || mod_value != string(v) {
panic("modify failed")
}
}
err = Diff(base_tree, head_tree, delete_handler, modify_handler, insert_handler)
mod_count = 0
modify_handler = func(k, v []byte) {
//t.Logf("value %s mod_value %s v %s" , value, mod_value, string(v))
mod_count++
if mod_count > 1 {
panic("more modified")
}
if key != string(k) || value != string(v) {
panic("modify failed")
}
}
err = Diff(head_tree, base_tree, delete_handler, modify_handler, insert_handler)
}
// this test various diff related codes
// tree needs to be a particuar case, as some paths are hardcoded and manually triggered
func TestDiffTree_Single_Key_error_cases(t *testing.T) {
store, err := NewMemStore()
require.NoError(t, err)
gv, err := store.LoadSnapshot(0)
require.NoError(t, err)
tree, err := gv.GetTree("root")
require.NoError(t, err)
rand.Seed(1000)
key := randString(32)
value := randString(8)
tree.Put([]byte(key), []byte(value))
tree.Commit() // commit the tree
for i := 0; i < 8; i++ {
key1 := randStr(60)
value1 := randStr(512)
tree.Put([]byte(key1), []byte(value1))
}
tree.Commit() // commit the tree
gv1, _ := store.LoadSnapshot(1)
base_tree, _ := gv1.GetTree("root")
//base_tree.graph("/tmp/11.dot")
gv2, _ := store.LoadSnapshot(2)
head_tree, _ := gv2.GetTree("root")
// let corrupt head tree
head_tree.root.findex = 1000000000
head_tree.root.loaded_partial = true
dt := diffTree{base_tree: base_tree, head_tree: head_tree}
require.Error(t, dt.compare_nodes(base_tree.root, head_tree.root, nil, nil, nil))
base_tree, _ = gv1.GetTree("root")
base_tree.root.findex = 1000000000
base_tree.root.loaded_partial = true
require.Error(t, dt.compare_nodes(base_tree.root, head_tree.root, nil, nil, nil))
base_tree, _ = gv1.GetTree("root")
head_tree, _ = gv2.GetTree("root")
head_tree.root.left.(*inner).findex = 1000000000
head_tree.root.left.(*inner).loaded_partial = true
require.Error(t, dt.compare_nodes(nil, head_tree.root, nil, nil, nil))
// t.Logf("basehash %x", base_tree.Hash())
// t.Logf("headhash %x", head_tree.Hash())
require.Error(t, Diff(base_tree, head_tree, nil, nil, nil))
}