mirror of https://github.com/etcd-io/bbolt.git
Fix quick tests.
parent
0cae98efc5
commit
8b3b81ef47
|
@ -41,7 +41,6 @@ func (c *Cursor) Get(key []byte) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If our target node isn't the same key as what's passed in then return nil.
|
// If our target node isn't the same key as what's passed in then return nil.
|
||||||
// c.page().hexdump(512)
|
|
||||||
if !bytes.Equal(key, c.node().key()) {
|
if !bytes.Equal(key, c.node().key()) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
38
db_test.go
38
db_test.go
|
@ -1,14 +1,11 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
"testing/quick"
|
|
||||||
"time"
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
@ -156,41 +153,6 @@ func TestDBPut(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that a bucket can write random keys and values across multiple txns.
|
|
||||||
func TestDBPutRandom(t *testing.T) {
|
|
||||||
f := func(items testKeyValuePairs) bool {
|
|
||||||
withOpenDB(func(db *DB, path string) {
|
|
||||||
db.CreateBucket("widgets")
|
|
||||||
for _, item := range items {
|
|
||||||
if len(item.Key) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := db.Put("widgets", item.Key, item.Value); err != nil {
|
|
||||||
panic("put error: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, item := range items {
|
|
||||||
if len(item.Key) == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
value, err := db.Get("widgets", item.Key)
|
|
||||||
if err != nil {
|
|
||||||
panic("get error: " + err.Error())
|
|
||||||
}
|
|
||||||
if !bytes.Equal(value, []byte(item.Value)) {
|
|
||||||
// db.CopyFile("/tmp/bolt.random.db")
|
|
||||||
t.Fatalf("value mismatch:\n%x\n%x", item.Value, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprint(os.Stderr, ".")
|
|
||||||
})
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if err := quick.Check(f, qc()); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that a bucket can delete an existing key.
|
// Ensure that a bucket can delete an existing key.
|
||||||
func TestDBDelete(t *testing.T) {
|
func TestDBDelete(t *testing.T) {
|
||||||
withOpenDB(func(db *DB, path string) {
|
withOpenDB(func(db *DB, path string) {
|
||||||
|
|
2
meta.go
2
meta.go
|
@ -1,6 +1,6 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
const magic uint32 = 0xDEADC0DE
|
const magic uint32 = 0xED0CDAED
|
||||||
|
|
||||||
type meta struct {
|
type meta struct {
|
||||||
magic uint32
|
magic uint32
|
||||||
|
|
12
node.go
12
node.go
|
@ -148,14 +148,16 @@ func (n *node) split(pageSize int) []*node {
|
||||||
threshold := pageSize / 2
|
threshold := pageSize / 2
|
||||||
|
|
||||||
// Group into smaller pages and target a given fill size.
|
// Group into smaller pages and target a given fill size.
|
||||||
size := 0
|
size := pageHeaderSize
|
||||||
current := &node{isLeaf: n.isLeaf}
|
inodes := n.inodes
|
||||||
nodes := make([]*node, 0)
|
current := n
|
||||||
|
current.inodes = nil
|
||||||
|
var nodes []*node
|
||||||
|
|
||||||
for i, inode := range n.inodes {
|
for i, inode := range inodes {
|
||||||
elemSize := n.pageElementSize() + len(inode.key) + len(inode.value)
|
elemSize := n.pageElementSize() + len(inode.key) + len(inode.value)
|
||||||
|
|
||||||
if len(current.inodes) >= minKeysPerPage && i < len(n.inodes)-minKeysPerPage && size+elemSize > threshold {
|
if len(current.inodes) >= minKeysPerPage && i < len(inodes)-minKeysPerPage && size+elemSize > threshold {
|
||||||
size = pageHeaderSize
|
size = pageHeaderSize
|
||||||
nodes = append(nodes, current)
|
nodes = append(nodes, current)
|
||||||
current = &node{isLeaf: n.isLeaf}
|
current = &node{isLeaf: n.isLeaf}
|
||||||
|
|
|
@ -91,7 +91,7 @@ func TestNodeSplit(t *testing.T) {
|
||||||
n.put([]byte("00000004"), []byte("00000004"), []byte("0123456701234567"), 0)
|
n.put([]byte("00000004"), []byte("00000004"), []byte("0123456701234567"), 0)
|
||||||
n.put([]byte("00000005"), []byte("00000005"), []byte("0123456701234567"), 0)
|
n.put([]byte("00000005"), []byte("00000005"), []byte("0123456701234567"), 0)
|
||||||
|
|
||||||
// Split between 3 & 4.
|
// Split between 2 & 3.
|
||||||
nodes := n.split(100)
|
nodes := n.split(100)
|
||||||
|
|
||||||
assert.Equal(t, len(nodes), 2)
|
assert.Equal(t, len(nodes), 2)
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"testing"
|
||||||
"testing/quick"
|
"testing/quick"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -18,45 +22,78 @@ import (
|
||||||
// -quick.maxvsize The maximum size of a value.
|
// -quick.maxvsize The maximum size of a value.
|
||||||
//
|
//
|
||||||
|
|
||||||
var seed, testMaxItemCount, testMaxKeySize, testMaxValueSize int
|
var qseed, qmaxitems, qmaxksize, qmaxvsize int
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
flag.IntVar(&seed, "quick.seed", int(time.Now().UnixNano())%100000, "")
|
flag.IntVar(&qseed, "quick.seed", int(time.Now().UnixNano())%100000, "")
|
||||||
flag.IntVar(&testMaxItemCount, "quick.maxitems", 1024, "")
|
flag.IntVar(&qmaxitems, "quick.maxitems", 1000, "")
|
||||||
flag.IntVar(&testMaxKeySize, "quick.maxksize", 1024, "")
|
flag.IntVar(&qmaxksize, "quick.maxksize", 1024, "")
|
||||||
flag.IntVar(&testMaxValueSize, "quick.maxvsize", 1024, "")
|
flag.IntVar(&qmaxvsize, "quick.maxvsize", 1024, "")
|
||||||
warn("seed:", seed)
|
flag.Parse()
|
||||||
|
warn("seed:", qseed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// qc creates a testing/quick configuration.
|
// Ensure that a bucket can write random keys and values across multiple txns.
|
||||||
func qc() *quick.Config {
|
func TestQuickPut(t *testing.T) {
|
||||||
return &quick.Config{Rand: rand.New(rand.NewSource(int64(seed)))}
|
index := 0
|
||||||
|
f := func(items testdata) bool {
|
||||||
|
withOpenDB(func(db *DB, path string) {
|
||||||
|
m := make(map[string][]byte)
|
||||||
|
|
||||||
|
db.CreateBucket("widgets")
|
||||||
|
|
||||||
|
for _, item := range items {
|
||||||
|
if err := db.Put("widgets", item.Key, item.Value); err != nil {
|
||||||
|
panic("put error: " + err.Error())
|
||||||
|
}
|
||||||
|
m[string(item.Key)] = item.Value
|
||||||
|
|
||||||
|
// Verify all key/values so far.
|
||||||
|
i := 0
|
||||||
|
for k, v := range m {
|
||||||
|
value, err := db.Get("widgets", []byte(k))
|
||||||
|
if err != nil {
|
||||||
|
panic("get error: " + err.Error())
|
||||||
|
}
|
||||||
|
if !bytes.Equal(value, v) {
|
||||||
|
db.CopyFile("/tmp/bolt.random.db")
|
||||||
|
t.Fatalf("value mismatch [run %d] (%d of %d):\nkey: %x\ngot: %x\nexp: %x", index, i, len(m), []byte(k), v, value)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprint(os.Stderr, ".")
|
||||||
|
})
|
||||||
|
index++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if err := quick.Check(f, &quick.Config{Rand: rand.New(rand.NewSource(int64(qseed)))}); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
fmt.Fprint(os.Stderr, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
type testKeyValuePairs []testKeyValuePair
|
type testdata []testdataitem
|
||||||
|
|
||||||
func (t testKeyValuePairs) Generate(rand *rand.Rand, size int) reflect.Value {
|
func (t testdata) Generate(rand *rand.Rand, size int) reflect.Value {
|
||||||
n := rand.Intn(testMaxItemCount-1) + 1
|
n := rand.Intn(qmaxitems-1) + 1
|
||||||
items := make(testKeyValuePairs, n)
|
items := make(testdata, n)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
items[i].Generate(rand, size)
|
item := &items[i]
|
||||||
|
item.Key = randByteSlice(rand, 1, qmaxksize)
|
||||||
|
item.Value = randByteSlice(rand, 0, qmaxvsize)
|
||||||
}
|
}
|
||||||
return reflect.ValueOf(items)
|
return reflect.ValueOf(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
type testKeyValuePair struct {
|
type testdataitem struct {
|
||||||
Key []byte
|
Key []byte
|
||||||
Value []byte
|
Value []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t testKeyValuePair) Generate(rand *rand.Rand, size int) reflect.Value {
|
|
||||||
t.Key = randByteSlice(rand, 1, testMaxKeySize)
|
|
||||||
t.Value = randByteSlice(rand, 0, testMaxValueSize)
|
|
||||||
return reflect.ValueOf(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func randByteSlice(rand *rand.Rand, minSize, maxSize int) []byte {
|
func randByteSlice(rand *rand.Rand, minSize, maxSize int) []byte {
|
||||||
n := rand.Intn(maxSize - minSize) + minSize
|
n := rand.Intn(maxSize-minSize) + minSize
|
||||||
b := make([]byte, n)
|
b := make([]byte, n)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
b[i] = byte(rand.Intn(255))
|
b[i] = byte(rand.Intn(255))
|
||||||
|
|
|
@ -247,7 +247,10 @@ func (t *RWTransaction) write() error {
|
||||||
for _, p := range pages {
|
for _, p := range pages {
|
||||||
size := (int(p.overflow) + 1) * t.db.pageSize
|
size := (int(p.overflow) + 1) * t.db.pageSize
|
||||||
buf := (*[maxAllocSize]byte)(unsafe.Pointer(p))[:size]
|
buf := (*[maxAllocSize]byte)(unsafe.Pointer(p))[:size]
|
||||||
t.db.file.WriteAt(buf, int64(p.id)*int64(t.db.pageSize))
|
offset := int64(p.id) * int64(t.db.pageSize)
|
||||||
|
if _, err := t.db.file.WriteAt(buf, offset); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
10
sys.go
10
sys.go
|
@ -25,16 +25,6 @@ func (s *sys) get(key string) *bucket {
|
||||||
return s.buckets[key]
|
return s.buckets[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
// getByRoot retrieves a bucket by root page id.
|
|
||||||
func (s *sys) getByRoot(pgid pgid) *bucket {
|
|
||||||
for _, b := range s.buckets {
|
|
||||||
if b.root == pgid {
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic("root not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// put sets a new value for a bucket.
|
// put sets a new value for a bucket.
|
||||||
func (s *sys) put(key string, b *bucket) {
|
func (s *sys) put(key string, b *bucket) {
|
||||||
s.buckets[key] = b
|
s.buckets[key] = b
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package bolt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure that a Transaction can retrieve a bucket.
|
||||||
|
func TestTransactionBucketMissing(t *testing.T) {
|
||||||
|
withOpenDB(func(db *DB, path string) {
|
||||||
|
db.CreateBucket("widgets")
|
||||||
|
b, err := db.Bucket("widgets")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
if assert.NotNil(t, b) {
|
||||||
|
assert.Equal(t, "widgets", b.Name())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that a Transaction retrieving a non-existent key returns nil.
|
||||||
|
func TestTransactionGetMising(t *testing.T) {
|
||||||
|
withOpenDB(func(db *DB, path string) {
|
||||||
|
db.CreateBucket("widgets")
|
||||||
|
db.Put("widgets", []byte("foo"), []byte("bar"))
|
||||||
|
value, err := db.Get("widgets", []byte("no_such_key"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Nil(t, value)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue