You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

256 lines
5.3 KiB

package db
import (
"bytes"
"encoding/binary"
"fmt"
"io/ioutil"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
cmn "github.com/tendermint/tendermint/libs/common"
)
//----------------------------------------
// Helper functions.
func checkValue(t *testing.T, db DB, key []byte, valueWanted []byte) {
valueGot := db.Get(key)
assert.Equal(t, valueWanted, valueGot)
}
func checkValid(t *testing.T, itr Iterator, expected bool) {
valid := itr.Valid()
require.Equal(t, expected, valid)
}
func checkNext(t *testing.T, itr Iterator, expected bool) {
itr.Next()
valid := itr.Valid()
require.Equal(t, expected, valid)
}
func checkNextPanics(t *testing.T, itr Iterator) {
assert.Panics(t, func() { itr.Next() }, "checkNextPanics expected panic but didn't")
}
func checkDomain(t *testing.T, itr Iterator, start, end []byte) {
ds, de := itr.Domain()
assert.Equal(t, start, ds, "checkDomain domain start incorrect")
assert.Equal(t, end, de, "checkDomain domain end incorrect")
}
func checkItem(t *testing.T, itr Iterator, key []byte, value []byte) {
k, v := itr.Key(), itr.Value()
assert.Exactly(t, key, k)
assert.Exactly(t, value, v)
}
func checkInvalid(t *testing.T, itr Iterator) {
checkValid(t, itr, false)
checkKeyPanics(t, itr)
checkValuePanics(t, itr)
checkNextPanics(t, itr)
}
func checkKeyPanics(t *testing.T, itr Iterator) {
assert.Panics(t, func() { itr.Key() }, "checkKeyPanics expected panic but didn't")
}
func checkValuePanics(t *testing.T, itr Iterator) {
assert.Panics(t, func() { itr.Value() }, "checkValuePanics expected panic but didn't")
}
func newTempDB(t *testing.T, backend DBBackendType) (db DB, dbDir string) {
dirname, err := ioutil.TempDir("", "db_common_test")
require.Nil(t, err)
return NewDB("testdb", backend, dirname), dirname
}
//----------------------------------------
// mockDB
// NOTE: not actually goroutine safe.
// If you want something goroutine safe, maybe you just want a MemDB.
type mockDB struct {
mtx sync.Mutex
calls map[string]int
}
func newMockDB() *mockDB {
return &mockDB{
calls: make(map[string]int),
}
}
func (mdb *mockDB) Mutex() *sync.Mutex {
return &(mdb.mtx)
}
func (mdb *mockDB) Get([]byte) []byte {
mdb.calls["Get"]++
return nil
}
func (mdb *mockDB) Has([]byte) bool {
mdb.calls["Has"]++
return false
}
func (mdb *mockDB) Set([]byte, []byte) {
mdb.calls["Set"]++
}
func (mdb *mockDB) SetSync([]byte, []byte) {
mdb.calls["SetSync"]++
}
func (mdb *mockDB) SetNoLock([]byte, []byte) {
mdb.calls["SetNoLock"]++
}
func (mdb *mockDB) SetNoLockSync([]byte, []byte) {
mdb.calls["SetNoLockSync"]++
}
func (mdb *mockDB) Delete([]byte) {
mdb.calls["Delete"]++
}
func (mdb *mockDB) DeleteSync([]byte) {
mdb.calls["DeleteSync"]++
}
func (mdb *mockDB) DeleteNoLock([]byte) {
mdb.calls["DeleteNoLock"]++
}
func (mdb *mockDB) DeleteNoLockSync([]byte) {
mdb.calls["DeleteNoLockSync"]++
}
func (mdb *mockDB) Iterator(start, end []byte) Iterator {
mdb.calls["Iterator"]++
return &mockIterator{}
}
func (mdb *mockDB) ReverseIterator(start, end []byte) Iterator {
mdb.calls["ReverseIterator"]++
return &mockIterator{}
}
func (mdb *mockDB) Close() {
mdb.calls["Close"]++
}
func (mdb *mockDB) NewBatch() Batch {
mdb.calls["NewBatch"]++
return &memBatch{db: mdb}
}
func (mdb *mockDB) Print() {
mdb.calls["Print"]++
fmt.Printf("mockDB{%v}", mdb.Stats())
}
func (mdb *mockDB) Stats() map[string]string {
mdb.calls["Stats"]++
res := make(map[string]string)
for key, count := range mdb.calls {
res[key] = fmt.Sprintf("%d", count)
}
return res
}
//----------------------------------------
// mockIterator
type mockIterator struct{}
func (mockIterator) Domain() (start []byte, end []byte) {
return nil, nil
}
func (mockIterator) Valid() bool {
return false
}
func (mockIterator) Next() {
}
func (mockIterator) Key() []byte {
return nil
}
func (mockIterator) Value() []byte {
return nil
}
func (mockIterator) Close() {
}
func benchmarkRandomReadsWrites(b *testing.B, db DB) {
b.StopTimer()
// create dummy data
const numItems = int64(1000000)
internal := map[int64]int64{}
for i := 0; i < int(numItems); i++ {
internal[int64(i)] = int64(0)
}
// fmt.Println("ok, starting")
b.StartTimer()
for i := 0; i < b.N; i++ {
// Write something
{
idx := int64(cmn.RandInt()) % numItems
internal[idx]++
val := internal[idx]
idxBytes := int642Bytes(int64(idx))
valBytes := int642Bytes(int64(val))
//fmt.Printf("Set %X -> %X\n", idxBytes, valBytes)
db.Set(idxBytes, valBytes)
}
// Read something
{
idx := int64(cmn.RandInt()) % numItems
valExp := internal[idx]
idxBytes := int642Bytes(int64(idx))
valBytes := db.Get(idxBytes)
//fmt.Printf("Get %X -> %X\n", idxBytes, valBytes)
if valExp == 0 {
if !bytes.Equal(valBytes, nil) {
b.Errorf("Expected %v for %v, got %X", nil, idx, valBytes)
break
}
} else {
if len(valBytes) != 8 {
b.Errorf("Expected length 8 for %v, got %X", idx, valBytes)
break
}
valGot := bytes2Int64(valBytes)
if valExp != valGot {
b.Errorf("Expected %v for %v, got %v", valExp, idx, valGot)
break
}
}
}
}
}
func int642Bytes(i int64) []byte {
buf := make([]byte, 8)
binary.BigEndian.PutUint64(buf, uint64(i))
return buf
}
func bytes2Int64(buf []byte) int64 {
return int64(binary.BigEndian.Uint64(buf))
}