Fix quick tests.

pull/34/head
Ben Johnson 2014-02-04 15:30:05 -07:00
parent 0cae98efc5
commit 8b3b81ef47
9 changed files with 102 additions and 79 deletions

View File

@ -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
} }

View File

@ -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) {

View File

@ -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
View File

@ -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}

View File

@ -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)

View File

@ -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))

View File

@ -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
View File

@ -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

30
transaction_test.go Normal file
View File

@ -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)
})
}