aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Johnson <benbjohnson@yahoo.com>2014-02-15 23:38:03 -0700
committerBen Johnson <benbjohnson@yahoo.com>2014-02-15 23:45:17 -0700
commitd1952237edfbc3e2ab93a1109537c68144e9fd1e (patch)
tree76ad78453cbf13d069a9aa8c6490fd26da43aac1
parentFix DB.opened flag. (diff)
downloaddedo-d1952237edfbc3e2ab93a1109537c68144e9fd1e.tar.gz
dedo-d1952237edfbc3e2ab93a1109537c68144e9fd1e.tar.xz
Improve test coverage.
-rw-r--r--cursor.go4
-rw-r--r--db.go17
-rw-r--r--db_test.go67
-rw-r--r--functional_test.go5
-rw-r--r--rwtransaction_test.go56
-rw-r--r--transaction.go8
-rw-r--r--transaction_test.go12
7 files changed, 128 insertions, 41 deletions
diff --git a/cursor.go b/cursor.go
index a8a71b1..109cffb 100644
--- a/cursor.go
+++ b/cursor.go
@@ -154,9 +154,7 @@ func (c *Cursor) keyValue() ([]byte, []byte) {
// node returns the node that the cursor is currently positioned on.
func (c *Cursor) node(t *RWTransaction) *node {
- if len(c.stack) == 0 {
- return nil
- }
+ _assert(len(c.stack) > 0, "accessing a node with a zero-length cursor stack")
// Start from root and traverse down the hierarchy.
n := t.node(c.stack[0].page.id, nil)
diff --git a/db.go b/db.go
index 399e39b..99cc9ca 100644
--- a/db.go
+++ b/db.go
@@ -240,7 +240,7 @@ func (db *DB) Close() {
func (db *DB) close() {
db.opened = false
-
+
// TODO(benbjohnson): Undo everything in Open().
db.freelist = nil
db.path = ""
@@ -432,10 +432,6 @@ func (db *DB) Delete(name string, key []byte) error {
// A reader transaction is maintained during the copy so it is safe to continue
// using the database while a copy is in progress.
func (db *DB) Copy(w io.Writer) error {
- if !db.opened {
- return DatabaseNotOpenError
- }
-
// Maintain a reader transaction so pages don't get reclaimed.
t, err := db.Transaction()
if err != nil {
@@ -514,14 +510,3 @@ func (db *DB) allocate(count int) (*page, error) {
return p, nil
}
-
-// sync flushes the file descriptor to disk.
-func (db *DB) sync(force bool) error {
- if db.opened {
- return DatabaseNotOpenError
- }
- if err := syscall.Fsync(int(db.file.Fd())); err != nil {
- return err
- }
- return nil
-}
diff --git a/db_test.go b/db_test.go
index 666ba4a..89da00f 100644
--- a/db_test.go
+++ b/db_test.go
@@ -178,6 +178,14 @@ func TestDBDelete(t *testing.T) {
})
}
+// Ensure that a delete on a missing bucket returns an error.
+func TestDBDeleteFromMissingBucket(t *testing.T) {
+ withOpenDB(func(db *DB, path string) {
+ err := db.Delete("widgets", []byte("foo"))
+ assert.Equal(t, err, BucketNotFoundError)
+ })
+}
+
// Ensure a database can provide a transactional block.
func TestDBTransactionBlock(t *testing.T) {
withOpenDB(func(db *DB, path string) {
@@ -196,19 +204,62 @@ func TestDBTransactionBlock(t *testing.T) {
})
}
-// Ensure that the database can be copied to a writer.
-func TestDBCopy(t *testing.T) {
- t.Skip("pending") // TODO(benbjohnson)
+// Ensure a closed database returns an error while running a transaction block
+func TestDBTransactionBlockWhileClosed(t *testing.T) {
+ withDB(func(db *DB, path string) {
+ err := db.Do(func(txn *RWTransaction) error {
+ txn.CreateBucket("widgets")
+ return nil
+ })
+ assert.Equal(t, err, DatabaseNotOpenError)
+ })
+}
+
+// Ensure a closed database returns an error when finding a bucket.
+func TestDBBucketWhileClosed(t *testing.T) {
+ withDB(func(db *DB, path string) {
+ b, err := db.Bucket("widgets")
+ assert.Equal(t, err, DatabaseNotOpenError)
+ assert.Nil(t, b)
+ })
+}
+
+// Ensure a closed database returns an error when finding all buckets.
+func TestDBBucketsWhileClosed(t *testing.T) {
+ withDB(func(db *DB, path string) {
+ b, err := db.Buckets()
+ assert.Equal(t, err, DatabaseNotOpenError)
+ assert.Nil(t, b)
+ })
+}
+
+// Ensure a closed database returns an error when getting a key.
+func TestDBGetWhileClosed(t *testing.T) {
+ withDB(func(db *DB, path string) {
+ value, err := db.Get("widgets", []byte("foo"))
+ assert.Equal(t, err, DatabaseNotOpenError)
+ assert.Nil(t, value)
+ })
}
// Ensure that the database can be copied to a file path.
func TestDBCopyFile(t *testing.T) {
- t.Skip("pending") // TODO(benbjohnson)
-}
+ withOpenDB(func(db *DB, path string) {
+ db.CreateBucket("widgets")
+ db.Put("widgets", []byte("foo"), []byte("bar"))
+ db.Put("widgets", []byte("baz"), []byte("bat"))
+ assert.NoError(t, os.RemoveAll("/tmp/bolt.copyfile.db"))
+ assert.NoError(t, db.CopyFile("/tmp/bolt.copyfile.db", 0666))
-// Ensure that the database can sync to the file system.
-func TestDBSync(t *testing.T) {
- t.Skip("pending") // TODO(benbjohnson)
+ var db2 DB
+ assert.NoError(t, db2.Open("/tmp/bolt.copyfile.db", 0666))
+ defer db2.Close()
+
+ value, _ := db2.Get("widgets", []byte("foo"))
+ assert.Equal(t, value, []byte("bar"))
+ value, _ = db2.Get("widgets", []byte("baz"))
+ assert.Equal(t, value, []byte("bat"))
+ })
}
// Ensure that an error is returned when a database write fails.
diff --git a/functional_test.go b/functional_test.go
index e155bc3..f0b18cf 100644
--- a/functional_test.go
+++ b/functional_test.go
@@ -47,7 +47,10 @@ func TestParallelTransactions(t *testing.T) {
local := current
txn, err := db.Transaction()
mutex.RUnlock()
- if !assert.NoError(t, err) {
+ if err == DatabaseNotOpenError {
+ wg.Done()
+ return
+ } else if !assert.NoError(t, err) {
t.FailNow()
}
diff --git a/rwtransaction_test.go b/rwtransaction_test.go
index fa254d8..14ba6b2 100644
--- a/rwtransaction_test.go
+++ b/rwtransaction_test.go
@@ -21,6 +21,15 @@ func TestRWTransaction(t *testing.T) {
})
}
+// Ensure that opening a RWTransaction while the DB is closed returns an error.
+func TestRWTransactionOpenWithClosedDB(t *testing.T) {
+ withDB(func(db *DB, path string) {
+ txn, err := db.RWTransaction()
+ assert.Equal(t, err, DatabaseNotOpenError)
+ assert.Nil(t, txn)
+ })
+}
+
// Ensure that a bucket can be created and retrieved.
func TestRWTransactionCreateBucket(t *testing.T) {
withOpenDB(func(db *DB, path string) {
@@ -69,7 +78,23 @@ func TestRWTransactionCreateBucketWithLongName(t *testing.T) {
// Ensure that a bucket can be deleted.
func TestRWTransactionDeleteBucket(t *testing.T) {
- t.Skip("pending") // TODO(benbjohnson)
+ withOpenDB(func(db *DB, path string) {
+ // Create a bucket and add a value.
+ db.CreateBucket("widgets")
+ db.Put("widgets", []byte("foo"), []byte("bar"))
+
+ // Delete the bucket and make sure we can't get the value.
+ assert.NoError(t, db.DeleteBucket("widgets"))
+ value, err := db.Get("widgets", []byte("foo"))
+ assert.Equal(t, err, BucketNotFoundError)
+ assert.Nil(t, value)
+
+ // Create the bucket again and make sure there's not a phantom value.
+ assert.NoError(t, db.CreateBucket("widgets"))
+ value, err = db.Get("widgets", []byte("foo"))
+ assert.NoError(t, err)
+ assert.Nil(t, value)
+ })
}
// Ensure that a bucket can return an autoincrementing sequence.
@@ -100,27 +125,38 @@ func TestRWTransactionNextSequence(t *testing.T) {
// Ensure that an error is returned when inserting into a bucket that doesn't exist.
func TestRWTransactionPutBucketNotFound(t *testing.T) {
- t.Skip("pending") // TODO(benbjohnson)
+ withOpenDB(func(db *DB, path string) {
+ err := db.Put("widgets", []byte("foo"), []byte("bar"))
+ assert.Equal(t, err, BucketNotFoundError)
+ })
}
// Ensure that an error is returned when inserting with an empty key.
func TestRWTransactionPutEmptyKey(t *testing.T) {
- t.Skip("pending") // TODO(benbjohnson)
+ withOpenDB(func(db *DB, path string) {
+ db.CreateBucket("widgets")
+ err := db.Put("widgets", []byte(""), []byte("bar"))
+ assert.Equal(t, err, KeyRequiredError)
+ err = db.Put("widgets", nil, []byte("bar"))
+ assert.Equal(t, err, KeyRequiredError)
+ })
}
// Ensure that an error is returned when inserting with a key that's too large.
func TestRWTransactionPutKeyTooLarge(t *testing.T) {
- t.Skip("pending") // TODO(benbjohnson)
-}
-
-// Ensure that an error is returned when inserting with data that's too large.
-func TestRWTransactionPutDataTooLarge(t *testing.T) {
- t.Skip("pending") // TODO(benbjohnson)
+ withOpenDB(func(db *DB, path string) {
+ db.CreateBucket("widgets")
+ err := db.Put("widgets", make([]byte, 32769), []byte("bar"))
+ assert.Equal(t, err, KeyTooLargeError)
+ })
}
// Ensure that an error is returned when deleting from a bucket that doesn't exist.
func TestRWTransactionDeleteBucketNotFound(t *testing.T) {
- t.Skip("pending") // TODO(benbjohnson)
+ withOpenDB(func(db *DB, path string) {
+ err := db.DeleteBucket("widgets")
+ assert.Equal(t, err, BucketNotFoundError)
+ })
}
// Ensure that a bucket can write random keys and values across multiple txns.
diff --git a/transaction.go b/transaction.go
index 46adefe..739d112 100644
--- a/transaction.go
+++ b/transaction.go
@@ -63,8 +63,12 @@ func (t *Transaction) Bucket(name string) *Bucket {
// Buckets retrieves a list of all buckets.
func (t *Transaction) Buckets() []*Bucket {
- warn("[pending] Transaction.Buckets()") // TODO
- return nil
+ buckets := make([]*Bucket, 0, len(t.buckets.items))
+ for name, b := range t.buckets.items {
+ bucket := &Bucket{bucket: b, transaction: t, name: name}
+ buckets = append(buckets, bucket)
+ }
+ return buckets
}
// Cursor creates a cursor associated with a given bucket.
diff --git a/transaction_test.go b/transaction_test.go
index afe3b8e..5a7177f 100644
--- a/transaction_test.go
+++ b/transaction_test.go
@@ -12,7 +12,17 @@ import (
// Ensure that the database can retrieve a list of buckets.
func TestTransactionBuckets(t *testing.T) {
- t.Skip("pending") // TODO(benbjohnson)
+ withOpenDB(func(db *DB, path string) {
+ db.CreateBucket("foo")
+ db.CreateBucket("bar")
+ db.CreateBucket("baz")
+ buckets, err := db.Buckets()
+ if assert.NoError(t, err) && assert.Equal(t, len(buckets), 3) {
+ assert.Equal(t, buckets[0].Name(), "bar")
+ assert.Equal(t, buckets[1].Name(), "baz")
+ assert.Equal(t, buckets[2].Name(), "foo")
+ }
+ })
}
// Ensure that a Transaction can retrieve a bucket.