diff --git a/node.go b/node.go index a6cc334..8229ae3 100644 --- a/node.go +++ b/node.go @@ -40,7 +40,7 @@ func (n *node) root() *node { if n.parent == nil { return n } - return n.parent + return n.parent.root() } // put inserts a key/value. diff --git a/quick_test.go b/quick_test.go index e7fb22a..4a107a8 100644 --- a/quick_test.go +++ b/quick_test.go @@ -1,30 +1,27 @@ package bolt import ( - "bytes" "flag" - "fmt" "math/rand" - "os" "reflect" - "testing" "testing/quick" "time" ) -// testing/quick defaults to 100 iterations and a random seed. +// testing/quick defaults to 5 iterations and a random seed. // You can override these settings from the command line: // -// -quickchecks The number of iterations to perform. +// -quick.count The number of iterations to perform. // -quick.seed The seed to use for randomizing. // -quick.maxitems The maximum number of items to insert into a DB. // -quick.maxksize The maximum size of a key. // -quick.maxvsize The maximum size of a value. // -var qseed, qmaxitems, qmaxksize, qmaxvsize int +var qcount, qseed, qmaxitems, qmaxksize, qmaxvsize int func init() { + flag.IntVar(&qcount, "quick.count", 5, "") flag.IntVar(&qseed, "quick.seed", int(time.Now().UnixNano())%100000, "") flag.IntVar(&qmaxitems, "quick.maxitems", 1000, "") flag.IntVar(&qmaxksize, "quick.maxksize", 1024, "") @@ -33,45 +30,11 @@ func init() { warn("seed:", qseed) } -// Ensure that a bucket can write random keys and values across multiple txns. -func TestQuickPut(t *testing.T) { - 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 +func qconfig() *quick.Config { + return &quick.Config{ + MaxCount: qcount, + Rand: rand.New(rand.NewSource(int64(qseed))), } - 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 testdata []testdataitem diff --git a/rwtransaction.go b/rwtransaction.go index 505d107..d6cde58 100644 --- a/rwtransaction.go +++ b/rwtransaction.go @@ -204,7 +204,7 @@ func (t *RWTransaction) spill() { newNode.write(p) newNode.pgid = p.id newNode.parent = n.parent - + // The first node should use the existing entry, other nodes are inserts. var oldKey []byte if i == 0 { diff --git a/rwtransaction_test.go b/rwtransaction_test.go index 4d256cc..75a606b 100644 --- a/rwtransaction_test.go +++ b/rwtransaction_test.go @@ -1,12 +1,15 @@ package bolt import ( + "bytes" + "fmt" + "os" "strings" "testing" + "testing/quick" "github.com/stretchr/testify/assert" ) - // Ensure that a RWTransaction can be retrieved. func TestRWTransaction(t *testing.T) { withOpenDB(func(db *DB, path string) { @@ -61,3 +64,74 @@ func TestTransactionCreateBucketWithLongName(t *testing.T) { assert.Equal(t, err, &Error{"bucket name too long", nil}) }) } + +// Ensure that a bucket can write random keys and values across multiple txns. +func TestRWTransactionPutSingle(t *testing.T) { + 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.put.single.db") + t.Fatalf("value mismatch [run %d] (%d of %d):\nkey: %x\ngot: %x\nexp: %x", index, i, len(m), []byte(k), value, v) + } + i++ + } + } + + fmt.Fprint(os.Stderr, ".") + }) + index++ + return true + } + if err := quick.Check(f, qconfig()); err != nil { + t.Error(err) + } + fmt.Fprint(os.Stderr, "\n") +} + +// Ensure that a transaction can insert multiple key/value pairs at once. +func TestRWTransactionPutMultiple(t *testing.T) { + f := func(items testdata) bool { + withOpenDB(func(db *DB, path string) { + // Bulk insert all values. + db.CreateBucket("widgets") + rwtxn, _ := db.RWTransaction() + for _, item := range items { + assert.NoError(t, rwtxn.Put("widgets", item.Key, item.Value)) + } + assert.NoError(t, rwtxn.Commit()) + + // Verify all items exist. + txn, _ := db.Transaction() + for _, item := range items { + if !assert.Equal(t, item.Value, txn.Get("widgets", item.Key)) { + db.CopyFile("/tmp/bolt.put.multiple.db") + t.FailNow() + } + } + txn.Close() + }) + fmt.Fprint(os.Stderr, ".") + return true + } + if err := quick.Check(f, qconfig()); err != nil { + t.Error(err) + } + fmt.Fprint(os.Stderr, "\n") +}