mirror of
https://github.com/taigrr/bitcask
synced 2025-01-18 04:03:17 -08:00
127 lines
3.8 KiB
Go
127 lines
3.8 KiB
Go
package index
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/binary"
|
|
"testing"
|
|
|
|
"github.com/pkg/errors"
|
|
art "github.com/plar/go-adaptive-radix-tree"
|
|
"git.mills.io/prologic/bitcask/internal"
|
|
)
|
|
|
|
const (
|
|
base64SampleTree = "AAAABGFiY2QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARhYmNlAAAAAQAAAAAAAAABAAAAAAAAAAEAAAAEYWJjZgAAAAIAAAAAAAAAAgAAAAAAAAACAAAABGFiZ2QAAAADAAAAAAAAAAMAAAAAAAAAAw=="
|
|
)
|
|
|
|
func TestWriteIndex(t *testing.T) {
|
|
at, expectedSerializedSize := getSampleTree()
|
|
|
|
var b bytes.Buffer
|
|
err := writeIndex(at, &b)
|
|
if err != nil {
|
|
t.Fatalf("writing index failed: %v", err)
|
|
}
|
|
if b.Len() != expectedSerializedSize {
|
|
t.Fatalf("incorrect size of serialied index: expected %d, got: %d", expectedSerializedSize, b.Len())
|
|
}
|
|
sampleTreeBytes, _ := base64.StdEncoding.DecodeString(base64SampleTree)
|
|
if !bytes.Equal(b.Bytes(), sampleTreeBytes) {
|
|
t.Fatalf("unexpected serialization of the tree")
|
|
}
|
|
}
|
|
|
|
func TestReadIndex(t *testing.T) {
|
|
sampleTreeBytes, _ := base64.StdEncoding.DecodeString(base64SampleTree)
|
|
b := bytes.NewBuffer(sampleTreeBytes)
|
|
|
|
at := art.New()
|
|
err := readIndex(b, at, 1024)
|
|
if err != nil {
|
|
t.Fatalf("error while deserializing correct sample tree: %v", err)
|
|
}
|
|
|
|
atsample, _ := getSampleTree()
|
|
if atsample.Size() != at.Size() {
|
|
t.Fatalf("trees aren't the same size, expected %v, got %v", atsample.Size(), at.Size())
|
|
}
|
|
atsample.ForEach(func(node art.Node) bool {
|
|
_, found := at.Search(node.Key())
|
|
if !found {
|
|
t.Fatalf("expected node wasn't found: %s", node.Key())
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
|
|
func TestReadCorruptedData(t *testing.T) {
|
|
sampleBytes, _ := base64.StdEncoding.DecodeString(base64SampleTree)
|
|
|
|
t.Run("truncated", func(t *testing.T) {
|
|
table := []struct {
|
|
name string
|
|
err error
|
|
data []byte
|
|
}{
|
|
{name: "key-size-first-item", err: errTruncatedKeySize, data: sampleBytes[:2]},
|
|
{name: "key-data-second-item", err: errTruncatedKeyData, data: sampleBytes[:6]},
|
|
{name: "key-size-second-item", err: errTruncatedKeySize, data: sampleBytes[:(int32Size+4+fileIDSize+offsetSize+sizeSize)+2]},
|
|
{name: "key-data-second-item", err: errTruncatedKeyData, data: sampleBytes[:(int32Size+4+fileIDSize+offsetSize+sizeSize)+6]},
|
|
{name: "data", err: errTruncatedData, data: sampleBytes[:int32Size+4+(fileIDSize+offsetSize+sizeSize-3)]},
|
|
}
|
|
|
|
for i := range table {
|
|
t.Run(table[i].name, func(t *testing.T) {
|
|
bf := bytes.NewBuffer(table[i].data)
|
|
|
|
if err := readIndex(bf, art.New(), 1024); !IsIndexCorruption(err) || errors.Cause(err) != table[i].err {
|
|
t.Fatalf("expected %v, got %v", table[i].err, err)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("overflow", func(t *testing.T) {
|
|
overflowKeySize := make([]byte, len(sampleBytes))
|
|
copy(overflowKeySize, sampleBytes)
|
|
binary.BigEndian.PutUint32(overflowKeySize, 1025)
|
|
|
|
overflowDataSize := make([]byte, len(sampleBytes))
|
|
copy(overflowDataSize, sampleBytes)
|
|
binary.BigEndian.PutUint32(overflowDataSize[int32Size+4+fileIDSize+offsetSize:], 1025)
|
|
|
|
table := []struct {
|
|
name string
|
|
err error
|
|
maxKeySize uint32
|
|
data []byte
|
|
}{
|
|
{name: "key-data-overflow", err: errKeySizeTooLarge, maxKeySize: 1024, data: overflowKeySize},
|
|
}
|
|
|
|
for i := range table {
|
|
t.Run(table[i].name, func(t *testing.T) {
|
|
bf := bytes.NewBuffer(table[i].data)
|
|
|
|
if err := readIndex(bf, art.New(), table[i].maxKeySize); !IsIndexCorruption(err) || errors.Cause(err) != table[i].err {
|
|
t.Fatalf("expected %v, got %v", table[i].err, err)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
}
|
|
|
|
func getSampleTree() (art.Tree, int) {
|
|
at := art.New()
|
|
keys := [][]byte{[]byte("abcd"), []byte("abce"), []byte("abcf"), []byte("abgd")}
|
|
expectedSerializedSize := 0
|
|
for i := range keys {
|
|
at.Insert(keys[i], internal.Item{FileID: i, Offset: int64(i), Size: int64(i)})
|
|
expectedSerializedSize += int32Size + len(keys[i]) + fileIDSize + offsetSize + sizeSize
|
|
}
|
|
|
|
return at, expectedSerializedSize
|
|
}
|