OSDN Git Service

Merge pull request #201 from Bytom/v0.1
[bytom/vapor.git] / vendor / github.com / btcsuite / btcd / database / ffldb / interface_test.go
diff --git a/vendor/github.com/btcsuite/btcd/database/ffldb/interface_test.go b/vendor/github.com/btcsuite/btcd/database/ffldb/interface_test.go
deleted file mode 100644 (file)
index 4989913..0000000
+++ /dev/null
@@ -1,2301 +0,0 @@
-// Copyright (c) 2015-2016 The btcsuite developers
-// Use of this source code is governed by an ISC
-// license that can be found in the LICENSE file.
-
-// This file intended to be copied into each backend driver directory.  Each
-// driver should have their own driver_test.go file which creates a database and
-// invokes the testInterface function in this file to ensure the driver properly
-// implements the interface.
-//
-// NOTE: When copying this file into the backend driver folder, the package name
-// will need to be changed accordingly.
-
-package ffldb_test
-
-import (
-       "bytes"
-       "compress/bzip2"
-       "encoding/binary"
-       "fmt"
-       "io"
-       "os"
-       "path/filepath"
-       "reflect"
-       "sync/atomic"
-       "testing"
-       "time"
-
-       "github.com/btcsuite/btcd/chaincfg"
-       "github.com/btcsuite/btcd/chaincfg/chainhash"
-       "github.com/btcsuite/btcd/database"
-       "github.com/btcsuite/btcd/wire"
-       "github.com/btcsuite/btcutil"
-)
-
-var (
-       // blockDataNet is the expected network in the test block data.
-       blockDataNet = wire.MainNet
-
-       // blockDataFile is the path to a file containing the first 256 blocks
-       // of the block chain.
-       blockDataFile = filepath.Join("..", "testdata", "blocks1-256.bz2")
-
-       // errSubTestFail is used to signal that a sub test returned false.
-       errSubTestFail = fmt.Errorf("sub test failure")
-)
-
-// loadBlocks loads the blocks contained in the testdata directory and returns
-// a slice of them.
-func loadBlocks(t *testing.T, dataFile string, network wire.BitcoinNet) ([]*btcutil.Block, error) {
-       // Open the file that contains the blocks for reading.
-       fi, err := os.Open(dataFile)
-       if err != nil {
-               t.Errorf("failed to open file %v, err %v", dataFile, err)
-               return nil, err
-       }
-       defer func() {
-               if err := fi.Close(); err != nil {
-                       t.Errorf("failed to close file %v %v", dataFile,
-                               err)
-               }
-       }()
-       dr := bzip2.NewReader(fi)
-
-       // Set the first block as the genesis block.
-       blocks := make([]*btcutil.Block, 0, 256)
-       genesis := btcutil.NewBlock(chaincfg.MainNetParams.GenesisBlock)
-       blocks = append(blocks, genesis)
-
-       // Load the remaining blocks.
-       for height := 1; ; height++ {
-               var net uint32
-               err := binary.Read(dr, binary.LittleEndian, &net)
-               if err == io.EOF {
-                       // Hit end of file at the expected offset.  No error.
-                       break
-               }
-               if err != nil {
-                       t.Errorf("Failed to load network type for block %d: %v",
-                               height, err)
-                       return nil, err
-               }
-               if net != uint32(network) {
-                       t.Errorf("Block doesn't match network: %v expects %v",
-                               net, network)
-                       return nil, err
-               }
-
-               var blockLen uint32
-               err = binary.Read(dr, binary.LittleEndian, &blockLen)
-               if err != nil {
-                       t.Errorf("Failed to load block size for block %d: %v",
-                               height, err)
-                       return nil, err
-               }
-
-               // Read the block.
-               blockBytes := make([]byte, blockLen)
-               _, err = io.ReadFull(dr, blockBytes)
-               if err != nil {
-                       t.Errorf("Failed to load block %d: %v", height, err)
-                       return nil, err
-               }
-
-               // Deserialize and store the block.
-               block, err := btcutil.NewBlockFromBytes(blockBytes)
-               if err != nil {
-                       t.Errorf("Failed to parse block %v: %v", height, err)
-                       return nil, err
-               }
-               blocks = append(blocks, block)
-       }
-
-       return blocks, nil
-}
-
-// checkDbError ensures the passed error is a database.Error with an error code
-// that matches the passed  error code.
-func checkDbError(t *testing.T, testName string, gotErr error, wantErrCode database.ErrorCode) bool {
-       dbErr, ok := gotErr.(database.Error)
-       if !ok {
-               t.Errorf("%s: unexpected error type - got %T, want %T",
-                       testName, gotErr, database.Error{})
-               return false
-       }
-       if dbErr.ErrorCode != wantErrCode {
-               t.Errorf("%s: unexpected error code - got %s (%s), want %s",
-                       testName, dbErr.ErrorCode, dbErr.Description,
-                       wantErrCode)
-               return false
-       }
-
-       return true
-}
-
-// testContext is used to store context information about a running test which
-// is passed into helper functions.
-type testContext struct {
-       t           *testing.T
-       db          database.DB
-       bucketDepth int
-       isWritable  bool
-       blocks      []*btcutil.Block
-}
-
-// keyPair houses a key/value pair.  It is used over maps so ordering can be
-// maintained.
-type keyPair struct {
-       key   []byte
-       value []byte
-}
-
-// lookupKey is a convenience method to lookup the requested key from the
-// provided keypair slice along with whether or not the key was found.
-func lookupKey(key []byte, values []keyPair) ([]byte, bool) {
-       for _, item := range values {
-               if bytes.Equal(item.key, key) {
-                       return item.value, true
-               }
-       }
-
-       return nil, false
-}
-
-// toGetValues returns a copy of the provided keypairs with all of the nil
-// values set to an empty byte slice.  This is used to ensure that keys set to
-// nil values result in empty byte slices when retrieved instead of nil.
-func toGetValues(values []keyPair) []keyPair {
-       ret := make([]keyPair, len(values))
-       copy(ret, values)
-       for i := range ret {
-               if ret[i].value == nil {
-                       ret[i].value = make([]byte, 0)
-               }
-       }
-       return ret
-}
-
-// rollbackValues returns a copy of the provided keypairs with all values set to
-// nil.  This is used to test that values are properly rolled back.
-func rollbackValues(values []keyPair) []keyPair {
-       ret := make([]keyPair, len(values))
-       copy(ret, values)
-       for i := range ret {
-               ret[i].value = nil
-       }
-       return ret
-}
-
-// testCursorKeyPair checks that the provide key and value match the expected
-// keypair at the provided index.  It also ensures the index is in range for the
-// provided slice of expected keypairs.
-func testCursorKeyPair(tc *testContext, k, v []byte, index int, values []keyPair) bool {
-       if index >= len(values) || index < 0 {
-               tc.t.Errorf("Cursor: exceeded the expected range of values - "+
-                       "index %d, num values %d", index, len(values))
-               return false
-       }
-
-       pair := &values[index]
-       if !bytes.Equal(k, pair.key) {
-               tc.t.Errorf("Mismatched cursor key: index %d does not match "+
-                       "the expected key - got %q, want %q", index, k,
-                       pair.key)
-               return false
-       }
-       if !bytes.Equal(v, pair.value) {
-               tc.t.Errorf("Mismatched cursor value: index %d does not match "+
-                       "the expected value - got %q, want %q", index, v,
-                       pair.value)
-               return false
-       }
-
-       return true
-}
-
-// testGetValues checks that all of the provided key/value pairs can be
-// retrieved from the database and the retrieved values match the provided
-// values.
-func testGetValues(tc *testContext, bucket database.Bucket, values []keyPair) bool {
-       for _, item := range values {
-               gotValue := bucket.Get(item.key)
-               if !reflect.DeepEqual(gotValue, item.value) {
-                       tc.t.Errorf("Get: unexpected value for %q - got %q, "+
-                               "want %q", item.key, gotValue, item.value)
-                       return false
-               }
-       }
-
-       return true
-}
-
-// testPutValues stores all of the provided key/value pairs in the provided
-// bucket while checking for errors.
-func testPutValues(tc *testContext, bucket database.Bucket, values []keyPair) bool {
-       for _, item := range values {
-               if err := bucket.Put(item.key, item.value); err != nil {
-                       tc.t.Errorf("Put: unexpected error: %v", err)
-                       return false
-               }
-       }
-
-       return true
-}
-
-// testDeleteValues removes all of the provided key/value pairs from the
-// provided bucket.
-func testDeleteValues(tc *testContext, bucket database.Bucket, values []keyPair) bool {
-       for _, item := range values {
-               if err := bucket.Delete(item.key); err != nil {
-                       tc.t.Errorf("Delete: unexpected error: %v", err)
-                       return false
-               }
-       }
-
-       return true
-}
-
-// testCursorInterface ensures the cursor itnerface is working properly by
-// exercising all of its functions on the passed bucket.
-func testCursorInterface(tc *testContext, bucket database.Bucket) bool {
-       // Ensure a cursor can be obtained for the bucket.
-       cursor := bucket.Cursor()
-       if cursor == nil {
-               tc.t.Error("Bucket.Cursor: unexpected nil cursor returned")
-               return false
-       }
-
-       // Ensure the cursor returns the same bucket it was created for.
-       if cursor.Bucket() != bucket {
-               tc.t.Error("Cursor.Bucket: does not match the bucket it was " +
-                       "created for")
-               return false
-       }
-
-       if tc.isWritable {
-               unsortedValues := []keyPair{
-                       {[]byte("cursor"), []byte("val1")},
-                       {[]byte("abcd"), []byte("val2")},
-                       {[]byte("bcd"), []byte("val3")},
-                       {[]byte("defg"), nil},
-               }
-               sortedValues := []keyPair{
-                       {[]byte("abcd"), []byte("val2")},
-                       {[]byte("bcd"), []byte("val3")},
-                       {[]byte("cursor"), []byte("val1")},
-                       {[]byte("defg"), nil},
-               }
-
-               // Store the values to be used in the cursor tests in unsorted
-               // order and ensure they were actually stored.
-               if !testPutValues(tc, bucket, unsortedValues) {
-                       return false
-               }
-               if !testGetValues(tc, bucket, toGetValues(unsortedValues)) {
-                       return false
-               }
-
-               // Ensure the cursor returns all items in byte-sorted order when
-               // iterating forward.
-               curIdx := 0
-               for ok := cursor.First(); ok; ok = cursor.Next() {
-                       k, v := cursor.Key(), cursor.Value()
-                       if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) {
-                               return false
-                       }
-                       curIdx++
-               }
-               if curIdx != len(unsortedValues) {
-                       tc.t.Errorf("Cursor: expected to iterate %d values, "+
-                               "but only iterated %d", len(unsortedValues),
-                               curIdx)
-                       return false
-               }
-
-               // Ensure the cursor returns all items in reverse byte-sorted
-               // order when iterating in reverse.
-               curIdx = len(sortedValues) - 1
-               for ok := cursor.Last(); ok; ok = cursor.Prev() {
-                       k, v := cursor.Key(), cursor.Value()
-                       if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) {
-                               return false
-                       }
-                       curIdx--
-               }
-               if curIdx > -1 {
-                       tc.t.Errorf("Reverse cursor: expected to iterate %d "+
-                               "values, but only iterated %d",
-                               len(sortedValues), len(sortedValues)-(curIdx+1))
-                       return false
-               }
-
-               // Ensure forward iteration works as expected after seeking.
-               middleIdx := (len(sortedValues) - 1) / 2
-               seekKey := sortedValues[middleIdx].key
-               curIdx = middleIdx
-               for ok := cursor.Seek(seekKey); ok; ok = cursor.Next() {
-                       k, v := cursor.Key(), cursor.Value()
-                       if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) {
-                               return false
-                       }
-                       curIdx++
-               }
-               if curIdx != len(sortedValues) {
-                       tc.t.Errorf("Cursor after seek: expected to iterate "+
-                               "%d values, but only iterated %d",
-                               len(sortedValues)-middleIdx, curIdx-middleIdx)
-                       return false
-               }
-
-               // Ensure reverse iteration works as expected after seeking.
-               curIdx = middleIdx
-               for ok := cursor.Seek(seekKey); ok; ok = cursor.Prev() {
-                       k, v := cursor.Key(), cursor.Value()
-                       if !testCursorKeyPair(tc, k, v, curIdx, sortedValues) {
-                               return false
-                       }
-                       curIdx--
-               }
-               if curIdx > -1 {
-                       tc.t.Errorf("Reverse cursor after seek: expected to "+
-                               "iterate %d values, but only iterated %d",
-                               len(sortedValues)-middleIdx, middleIdx-curIdx)
-                       return false
-               }
-
-               // Ensure the cursor deletes items properly.
-               if !cursor.First() {
-                       tc.t.Errorf("Cursor.First: no value")
-                       return false
-               }
-               k := cursor.Key()
-               if err := cursor.Delete(); err != nil {
-                       tc.t.Errorf("Cursor.Delete: unexpected error: %v", err)
-                       return false
-               }
-               if val := bucket.Get(k); val != nil {
-                       tc.t.Errorf("Cursor.Delete: value for key %q was not "+
-                               "deleted", k)
-                       return false
-               }
-       }
-
-       return true
-}
-
-// testNestedBucket reruns the testBucketInterface against a nested bucket along
-// with a counter to only test a couple of level deep.
-func testNestedBucket(tc *testContext, testBucket database.Bucket) bool {
-       // Don't go more than 2 nested levels deep.
-       if tc.bucketDepth > 1 {
-               return true
-       }
-
-       tc.bucketDepth++
-       defer func() {
-               tc.bucketDepth--
-       }()
-       return testBucketInterface(tc, testBucket)
-}
-
-// testBucketInterface ensures the bucket interface is working properly by
-// exercising all of its functions.  This includes the cursor interface for the
-// cursor returned from the bucket.
-func testBucketInterface(tc *testContext, bucket database.Bucket) bool {
-       if bucket.Writable() != tc.isWritable {
-               tc.t.Errorf("Bucket writable state does not match.")
-               return false
-       }
-
-       if tc.isWritable {
-               // keyValues holds the keys and values to use when putting
-               // values into the bucket.
-               keyValues := []keyPair{
-                       {[]byte("bucketkey1"), []byte("foo1")},
-                       {[]byte("bucketkey2"), []byte("foo2")},
-                       {[]byte("bucketkey3"), []byte("foo3")},
-                       {[]byte("bucketkey4"), nil},
-               }
-               expectedKeyValues := toGetValues(keyValues)
-               if !testPutValues(tc, bucket, keyValues) {
-                       return false
-               }
-
-               if !testGetValues(tc, bucket, expectedKeyValues) {
-                       return false
-               }
-
-               // Ensure errors returned from the user-supplied ForEach
-               // function are returned.
-               forEachError := fmt.Errorf("example foreach error")
-               err := bucket.ForEach(func(k, v []byte) error {
-                       return forEachError
-               })
-               if err != forEachError {
-                       tc.t.Errorf("ForEach: inner function error not "+
-                               "returned - got %v, want %v", err, forEachError)
-                       return false
-               }
-
-               // Iterate all of the keys using ForEach while making sure the
-               // stored values are the expected values.
-               keysFound := make(map[string]struct{}, len(keyValues))
-               err = bucket.ForEach(func(k, v []byte) error {
-                       wantV, found := lookupKey(k, expectedKeyValues)
-                       if !found {
-                               return fmt.Errorf("ForEach: key '%s' should "+
-                                       "exist", k)
-                       }
-
-                       if !reflect.DeepEqual(v, wantV) {
-                               return fmt.Errorf("ForEach: value for key '%s' "+
-                                       "does not match - got %s, want %s", k,
-                                       v, wantV)
-                       }
-
-                       keysFound[string(k)] = struct{}{}
-                       return nil
-               })
-               if err != nil {
-                       tc.t.Errorf("%v", err)
-                       return false
-               }
-
-               // Ensure all keys were iterated.
-               for _, item := range keyValues {
-                       if _, ok := keysFound[string(item.key)]; !ok {
-                               tc.t.Errorf("ForEach: key '%s' was not iterated "+
-                                       "when it should have been", item.key)
-                               return false
-                       }
-               }
-
-               // Delete the keys and ensure they were deleted.
-               if !testDeleteValues(tc, bucket, keyValues) {
-                       return false
-               }
-               if !testGetValues(tc, bucket, rollbackValues(keyValues)) {
-                       return false
-               }
-
-               // Ensure creating a new bucket works as expected.
-               testBucketName := []byte("testbucket")
-               testBucket, err := bucket.CreateBucket(testBucketName)
-               if err != nil {
-                       tc.t.Errorf("CreateBucket: unexpected error: %v", err)
-                       return false
-               }
-               if !testNestedBucket(tc, testBucket) {
-                       return false
-               }
-
-               // Ensure errors returned from the user-supplied ForEachBucket
-               // function are returned.
-               err = bucket.ForEachBucket(func(k []byte) error {
-                       return forEachError
-               })
-               if err != forEachError {
-                       tc.t.Errorf("ForEachBucket: inner function error not "+
-                               "returned - got %v, want %v", err, forEachError)
-                       return false
-               }
-
-               // Ensure creating a bucket that already exists fails with the
-               // expected error.
-               wantErrCode := database.ErrBucketExists
-               _, err = bucket.CreateBucket(testBucketName)
-               if !checkDbError(tc.t, "CreateBucket", err, wantErrCode) {
-                       return false
-               }
-
-               // Ensure CreateBucketIfNotExists returns an existing bucket.
-               testBucket, err = bucket.CreateBucketIfNotExists(testBucketName)
-               if err != nil {
-                       tc.t.Errorf("CreateBucketIfNotExists: unexpected "+
-                               "error: %v", err)
-                       return false
-               }
-               if !testNestedBucket(tc, testBucket) {
-                       return false
-               }
-
-               // Ensure retrieving an existing bucket works as expected.
-               testBucket = bucket.Bucket(testBucketName)
-               if !testNestedBucket(tc, testBucket) {
-                       return false
-               }
-
-               // Ensure deleting a bucket works as intended.
-               if err := bucket.DeleteBucket(testBucketName); err != nil {
-                       tc.t.Errorf("DeleteBucket: unexpected error: %v", err)
-                       return false
-               }
-               if b := bucket.Bucket(testBucketName); b != nil {
-                       tc.t.Errorf("DeleteBucket: bucket '%s' still exists",
-                               testBucketName)
-                       return false
-               }
-
-               // Ensure deleting a bucket that doesn't exist returns the
-               // expected error.
-               wantErrCode = database.ErrBucketNotFound
-               err = bucket.DeleteBucket(testBucketName)
-               if !checkDbError(tc.t, "DeleteBucket", err, wantErrCode) {
-                       return false
-               }
-
-               // Ensure CreateBucketIfNotExists creates a new bucket when
-               // it doesn't already exist.
-               testBucket, err = bucket.CreateBucketIfNotExists(testBucketName)
-               if err != nil {
-                       tc.t.Errorf("CreateBucketIfNotExists: unexpected "+
-                               "error: %v", err)
-                       return false
-               }
-               if !testNestedBucket(tc, testBucket) {
-                       return false
-               }
-
-               // Ensure the cursor interface works as expected.
-               if !testCursorInterface(tc, testBucket) {
-                       return false
-               }
-
-               // Delete the test bucket to avoid leaving it around for future
-               // calls.
-               if err := bucket.DeleteBucket(testBucketName); err != nil {
-                       tc.t.Errorf("DeleteBucket: unexpected error: %v", err)
-                       return false
-               }
-               if b := bucket.Bucket(testBucketName); b != nil {
-                       tc.t.Errorf("DeleteBucket: bucket '%s' still exists",
-                               testBucketName)
-                       return false
-               }
-       } else {
-               // Put should fail with bucket that is not writable.
-               testName := "unwritable tx put"
-               wantErrCode := database.ErrTxNotWritable
-               failBytes := []byte("fail")
-               err := bucket.Put(failBytes, failBytes)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // Delete should fail with bucket that is not writable.
-               testName = "unwritable tx delete"
-               err = bucket.Delete(failBytes)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // CreateBucket should fail with bucket that is not writable.
-               testName = "unwritable tx create bucket"
-               _, err = bucket.CreateBucket(failBytes)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // CreateBucketIfNotExists should fail with bucket that is not
-               // writable.
-               testName = "unwritable tx create bucket if not exists"
-               _, err = bucket.CreateBucketIfNotExists(failBytes)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // DeleteBucket should fail with bucket that is not writable.
-               testName = "unwritable tx delete bucket"
-               err = bucket.DeleteBucket(failBytes)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // Ensure the cursor interface works as expected with read-only
-               // buckets.
-               if !testCursorInterface(tc, bucket) {
-                       return false
-               }
-       }
-
-       return true
-}
-
-// rollbackOnPanic rolls the passed transaction back if the code in the calling
-// function panics.  This is useful in case the tests unexpectedly panic which
-// would leave any manually created transactions with the database mutex locked
-// thereby leading to a deadlock and masking the real reason for the panic.  It
-// also logs a test error and repanics so the original panic can be traced.
-func rollbackOnPanic(t *testing.T, tx database.Tx) {
-       if err := recover(); err != nil {
-               t.Errorf("Unexpected panic: %v", err)
-               _ = tx.Rollback()
-               panic(err)
-       }
-}
-
-// testMetadataManualTxInterface ensures that the manual transactions metadata
-// interface works as expected.
-func testMetadataManualTxInterface(tc *testContext) bool {
-       // populateValues tests that populating values works as expected.
-       //
-       // When the writable flag is false, a read-only tranasction is created,
-       // standard bucket tests for read-only transactions are performed, and
-       // the Commit function is checked to ensure it fails as expected.
-       //
-       // Otherwise, a read-write transaction is created, the values are
-       // written, standard bucket tests for read-write transactions are
-       // performed, and then the transaction is either committed or rolled
-       // back depending on the flag.
-       bucket1Name := []byte("bucket1")
-       populateValues := func(writable, rollback bool, putValues []keyPair) bool {
-               tx, err := tc.db.Begin(writable)
-               if err != nil {
-                       tc.t.Errorf("Begin: unexpected error %v", err)
-                       return false
-               }
-               defer rollbackOnPanic(tc.t, tx)
-
-               metadataBucket := tx.Metadata()
-               if metadataBucket == nil {
-                       tc.t.Errorf("Metadata: unexpected nil bucket")
-                       _ = tx.Rollback()
-                       return false
-               }
-
-               bucket1 := metadataBucket.Bucket(bucket1Name)
-               if bucket1 == nil {
-                       tc.t.Errorf("Bucket1: unexpected nil bucket")
-                       return false
-               }
-
-               tc.isWritable = writable
-               if !testBucketInterface(tc, bucket1) {
-                       _ = tx.Rollback()
-                       return false
-               }
-
-               if !writable {
-                       // The transaction is not writable, so it should fail
-                       // the commit.
-                       testName := "unwritable tx commit"
-                       wantErrCode := database.ErrTxNotWritable
-                       err := tx.Commit()
-                       if !checkDbError(tc.t, testName, err, wantErrCode) {
-                               _ = tx.Rollback()
-                               return false
-                       }
-               } else {
-                       if !testPutValues(tc, bucket1, putValues) {
-                               return false
-                       }
-
-                       if rollback {
-                               // Rollback the transaction.
-                               if err := tx.Rollback(); err != nil {
-                                       tc.t.Errorf("Rollback: unexpected "+
-                                               "error %v", err)
-                                       return false
-                               }
-                       } else {
-                               // The commit should succeed.
-                               if err := tx.Commit(); err != nil {
-                                       tc.t.Errorf("Commit: unexpected error "+
-                                               "%v", err)
-                                       return false
-                               }
-                       }
-               }
-
-               return true
-       }
-
-       // checkValues starts a read-only transaction and checks that all of
-       // the key/value pairs specified in the expectedValues parameter match
-       // what's in the database.
-       checkValues := func(expectedValues []keyPair) bool {
-               tx, err := tc.db.Begin(false)
-               if err != nil {
-                       tc.t.Errorf("Begin: unexpected error %v", err)
-                       return false
-               }
-               defer rollbackOnPanic(tc.t, tx)
-
-               metadataBucket := tx.Metadata()
-               if metadataBucket == nil {
-                       tc.t.Errorf("Metadata: unexpected nil bucket")
-                       _ = tx.Rollback()
-                       return false
-               }
-
-               bucket1 := metadataBucket.Bucket(bucket1Name)
-               if bucket1 == nil {
-                       tc.t.Errorf("Bucket1: unexpected nil bucket")
-                       return false
-               }
-
-               if !testGetValues(tc, bucket1, expectedValues) {
-                       _ = tx.Rollback()
-                       return false
-               }
-
-               // Rollback the read-only transaction.
-               if err := tx.Rollback(); err != nil {
-                       tc.t.Errorf("Commit: unexpected error %v", err)
-                       return false
-               }
-
-               return true
-       }
-
-       // deleteValues starts a read-write transaction and deletes the keys
-       // in the passed key/value pairs.
-       deleteValues := func(values []keyPair) bool {
-               tx, err := tc.db.Begin(true)
-               if err != nil {
-
-               }
-               defer rollbackOnPanic(tc.t, tx)
-
-               metadataBucket := tx.Metadata()
-               if metadataBucket == nil {
-                       tc.t.Errorf("Metadata: unexpected nil bucket")
-                       _ = tx.Rollback()
-                       return false
-               }
-
-               bucket1 := metadataBucket.Bucket(bucket1Name)
-               if bucket1 == nil {
-                       tc.t.Errorf("Bucket1: unexpected nil bucket")
-                       return false
-               }
-
-               // Delete the keys and ensure they were deleted.
-               if !testDeleteValues(tc, bucket1, values) {
-                       _ = tx.Rollback()
-                       return false
-               }
-               if !testGetValues(tc, bucket1, rollbackValues(values)) {
-                       _ = tx.Rollback()
-                       return false
-               }
-
-               // Commit the changes and ensure it was successful.
-               if err := tx.Commit(); err != nil {
-                       tc.t.Errorf("Commit: unexpected error %v", err)
-                       return false
-               }
-
-               return true
-       }
-
-       // keyValues holds the keys and values to use when putting values into a
-       // bucket.
-       var keyValues = []keyPair{
-               {[]byte("umtxkey1"), []byte("foo1")},
-               {[]byte("umtxkey2"), []byte("foo2")},
-               {[]byte("umtxkey3"), []byte("foo3")},
-               {[]byte("umtxkey4"), nil},
-       }
-
-       // Ensure that attempting populating the values using a read-only
-       // transaction fails as expected.
-       if !populateValues(false, true, keyValues) {
-               return false
-       }
-       if !checkValues(rollbackValues(keyValues)) {
-               return false
-       }
-
-       // Ensure that attempting populating the values using a read-write
-       // transaction and then rolling it back yields the expected values.
-       if !populateValues(true, true, keyValues) {
-               return false
-       }
-       if !checkValues(rollbackValues(keyValues)) {
-               return false
-       }
-
-       // Ensure that attempting populating the values using a read-write
-       // transaction and then committing it stores the expected values.
-       if !populateValues(true, false, keyValues) {
-               return false
-       }
-       if !checkValues(toGetValues(keyValues)) {
-               return false
-       }
-
-       // Clean up the keys.
-       if !deleteValues(keyValues) {
-               return false
-       }
-
-       return true
-}
-
-// testManagedTxPanics ensures calling Rollback of Commit inside a managed
-// transaction panics.
-func testManagedTxPanics(tc *testContext) bool {
-       testPanic := func(fn func()) (paniced bool) {
-               // Setup a defer to catch the expected panic and update the
-               // return variable.
-               defer func() {
-                       if err := recover(); err != nil {
-                               paniced = true
-                       }
-               }()
-
-               fn()
-               return false
-       }
-
-       // Ensure calling Commit on a managed read-only transaction panics.
-       paniced := testPanic(func() {
-               tc.db.View(func(tx database.Tx) error {
-                       tx.Commit()
-                       return nil
-               })
-       })
-       if !paniced {
-               tc.t.Error("Commit called inside View did not panic")
-               return false
-       }
-
-       // Ensure calling Rollback on a managed read-only transaction panics.
-       paniced = testPanic(func() {
-               tc.db.View(func(tx database.Tx) error {
-                       tx.Rollback()
-                       return nil
-               })
-       })
-       if !paniced {
-               tc.t.Error("Rollback called inside View did not panic")
-               return false
-       }
-
-       // Ensure calling Commit on a managed read-write transaction panics.
-       paniced = testPanic(func() {
-               tc.db.Update(func(tx database.Tx) error {
-                       tx.Commit()
-                       return nil
-               })
-       })
-       if !paniced {
-               tc.t.Error("Commit called inside Update did not panic")
-               return false
-       }
-
-       // Ensure calling Rollback on a managed read-write transaction panics.
-       paniced = testPanic(func() {
-               tc.db.Update(func(tx database.Tx) error {
-                       tx.Rollback()
-                       return nil
-               })
-       })
-       if !paniced {
-               tc.t.Error("Rollback called inside Update did not panic")
-               return false
-       }
-
-       return true
-}
-
-// testMetadataTxInterface tests all facets of the managed read/write and
-// manual transaction metadata interfaces as well as the bucket interfaces under
-// them.
-func testMetadataTxInterface(tc *testContext) bool {
-       if !testManagedTxPanics(tc) {
-               return false
-       }
-
-       bucket1Name := []byte("bucket1")
-       err := tc.db.Update(func(tx database.Tx) error {
-               _, err := tx.Metadata().CreateBucket(bucket1Name)
-               return err
-       })
-       if err != nil {
-               tc.t.Errorf("Update: unexpected error creating bucket: %v", err)
-               return false
-       }
-
-       if !testMetadataManualTxInterface(tc) {
-               return false
-       }
-
-       // keyValues holds the keys and values to use when putting values
-       // into a bucket.
-       keyValues := []keyPair{
-               {[]byte("mtxkey1"), []byte("foo1")},
-               {[]byte("mtxkey2"), []byte("foo2")},
-               {[]byte("mtxkey3"), []byte("foo3")},
-               {[]byte("mtxkey4"), nil},
-       }
-
-       // Test the bucket interface via a managed read-only transaction.
-       err = tc.db.View(func(tx database.Tx) error {
-               metadataBucket := tx.Metadata()
-               if metadataBucket == nil {
-                       return fmt.Errorf("Metadata: unexpected nil bucket")
-               }
-
-               bucket1 := metadataBucket.Bucket(bucket1Name)
-               if bucket1 == nil {
-                       return fmt.Errorf("Bucket1: unexpected nil bucket")
-               }
-
-               tc.isWritable = false
-               if !testBucketInterface(tc, bucket1) {
-                       return errSubTestFail
-               }
-
-               return nil
-       })
-       if err != nil {
-               if err != errSubTestFail {
-                       tc.t.Errorf("%v", err)
-               }
-               return false
-       }
-
-       // Ensure errors returned from the user-supplied View function are
-       // returned.
-       viewError := fmt.Errorf("example view error")
-       err = tc.db.View(func(tx database.Tx) error {
-               return viewError
-       })
-       if err != viewError {
-               tc.t.Errorf("View: inner function error not returned - got "+
-                       "%v, want %v", err, viewError)
-               return false
-       }
-
-       // Test the bucket interface via a managed read-write transaction.
-       // Also, put a series of values and force a rollback so the following
-       // code can ensure the values were not stored.
-       forceRollbackError := fmt.Errorf("force rollback")
-       err = tc.db.Update(func(tx database.Tx) error {
-               metadataBucket := tx.Metadata()
-               if metadataBucket == nil {
-                       return fmt.Errorf("Metadata: unexpected nil bucket")
-               }
-
-               bucket1 := metadataBucket.Bucket(bucket1Name)
-               if bucket1 == nil {
-                       return fmt.Errorf("Bucket1: unexpected nil bucket")
-               }
-
-               tc.isWritable = true
-               if !testBucketInterface(tc, bucket1) {
-                       return errSubTestFail
-               }
-
-               if !testPutValues(tc, bucket1, keyValues) {
-                       return errSubTestFail
-               }
-
-               // Return an error to force a rollback.
-               return forceRollbackError
-       })
-       if err != forceRollbackError {
-               if err == errSubTestFail {
-                       return false
-               }
-
-               tc.t.Errorf("Update: inner function error not returned - got "+
-                       "%v, want %v", err, forceRollbackError)
-               return false
-       }
-
-       // Ensure the values that should not have been stored due to the forced
-       // rollback above were not actually stored.
-       err = tc.db.View(func(tx database.Tx) error {
-               metadataBucket := tx.Metadata()
-               if metadataBucket == nil {
-                       return fmt.Errorf("Metadata: unexpected nil bucket")
-               }
-
-               if !testGetValues(tc, metadataBucket, rollbackValues(keyValues)) {
-                       return errSubTestFail
-               }
-
-               return nil
-       })
-       if err != nil {
-               if err != errSubTestFail {
-                       tc.t.Errorf("%v", err)
-               }
-               return false
-       }
-
-       // Store a series of values via a managed read-write transaction.
-       err = tc.db.Update(func(tx database.Tx) error {
-               metadataBucket := tx.Metadata()
-               if metadataBucket == nil {
-                       return fmt.Errorf("Metadata: unexpected nil bucket")
-               }
-
-               bucket1 := metadataBucket.Bucket(bucket1Name)
-               if bucket1 == nil {
-                       return fmt.Errorf("Bucket1: unexpected nil bucket")
-               }
-
-               if !testPutValues(tc, bucket1, keyValues) {
-                       return errSubTestFail
-               }
-
-               return nil
-       })
-       if err != nil {
-               if err != errSubTestFail {
-                       tc.t.Errorf("%v", err)
-               }
-               return false
-       }
-
-       // Ensure the values stored above were committed as expected.
-       err = tc.db.View(func(tx database.Tx) error {
-               metadataBucket := tx.Metadata()
-               if metadataBucket == nil {
-                       return fmt.Errorf("Metadata: unexpected nil bucket")
-               }
-
-               bucket1 := metadataBucket.Bucket(bucket1Name)
-               if bucket1 == nil {
-                       return fmt.Errorf("Bucket1: unexpected nil bucket")
-               }
-
-               if !testGetValues(tc, bucket1, toGetValues(keyValues)) {
-                       return errSubTestFail
-               }
-
-               return nil
-       })
-       if err != nil {
-               if err != errSubTestFail {
-                       tc.t.Errorf("%v", err)
-               }
-               return false
-       }
-
-       // Clean up the values stored above in a managed read-write transaction.
-       err = tc.db.Update(func(tx database.Tx) error {
-               metadataBucket := tx.Metadata()
-               if metadataBucket == nil {
-                       return fmt.Errorf("Metadata: unexpected nil bucket")
-               }
-
-               bucket1 := metadataBucket.Bucket(bucket1Name)
-               if bucket1 == nil {
-                       return fmt.Errorf("Bucket1: unexpected nil bucket")
-               }
-
-               if !testDeleteValues(tc, bucket1, keyValues) {
-                       return errSubTestFail
-               }
-
-               return nil
-       })
-       if err != nil {
-               if err != errSubTestFail {
-                       tc.t.Errorf("%v", err)
-               }
-               return false
-       }
-
-       return true
-}
-
-// testFetchBlockIOMissing ensures that all of the block retrieval API functions
-// work as expected when requesting blocks that don't exist.
-func testFetchBlockIOMissing(tc *testContext, tx database.Tx) bool {
-       wantErrCode := database.ErrBlockNotFound
-
-       // ---------------------
-       // Non-bulk Block IO API
-       // ---------------------
-
-       // Test the individual block APIs one block at a time to ensure they
-       // return the expected error.  Also, build the data needed to test the
-       // bulk APIs below while looping.
-       allBlockHashes := make([]chainhash.Hash, len(tc.blocks))
-       allBlockRegions := make([]database.BlockRegion, len(tc.blocks))
-       for i, block := range tc.blocks {
-               blockHash := block.Hash()
-               allBlockHashes[i] = *blockHash
-
-               txLocs, err := block.TxLoc()
-               if err != nil {
-                       tc.t.Errorf("block.TxLoc(%d): unexpected error: %v", i,
-                               err)
-                       return false
-               }
-
-               // Ensure FetchBlock returns expected error.
-               testName := fmt.Sprintf("FetchBlock #%d on missing block", i)
-               _, err = tx.FetchBlock(blockHash)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // Ensure FetchBlockHeader returns expected error.
-               testName = fmt.Sprintf("FetchBlockHeader #%d on missing block",
-                       i)
-               _, err = tx.FetchBlockHeader(blockHash)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // Ensure the first transaction fetched as a block region from
-               // the database returns the expected error.
-               region := database.BlockRegion{
-                       Hash:   blockHash,
-                       Offset: uint32(txLocs[0].TxStart),
-                       Len:    uint32(txLocs[0].TxLen),
-               }
-               allBlockRegions[i] = region
-               _, err = tx.FetchBlockRegion(&region)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // Ensure HasBlock returns false.
-               hasBlock, err := tx.HasBlock(blockHash)
-               if err != nil {
-                       tc.t.Errorf("HasBlock #%d: unexpected err: %v", i, err)
-                       return false
-               }
-               if hasBlock {
-                       tc.t.Errorf("HasBlock #%d: should not have block", i)
-                       return false
-               }
-       }
-
-       // -----------------
-       // Bulk Block IO API
-       // -----------------
-
-       // Ensure FetchBlocks returns expected error.
-       testName := "FetchBlocks on missing blocks"
-       _, err := tx.FetchBlocks(allBlockHashes)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure FetchBlockHeaders returns expected error.
-       testName = "FetchBlockHeaders on missing blocks"
-       _, err = tx.FetchBlockHeaders(allBlockHashes)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure FetchBlockRegions returns expected error.
-       testName = "FetchBlockRegions on missing blocks"
-       _, err = tx.FetchBlockRegions(allBlockRegions)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure HasBlocks returns false for all blocks.
-       hasBlocks, err := tx.HasBlocks(allBlockHashes)
-       if err != nil {
-               tc.t.Errorf("HasBlocks: unexpected err: %v", err)
-       }
-       for i, hasBlock := range hasBlocks {
-               if hasBlock {
-                       tc.t.Errorf("HasBlocks #%d: should not have block", i)
-                       return false
-               }
-       }
-
-       return true
-}
-
-// testFetchBlockIO ensures all of the block retrieval API functions work as
-// expected for the provide set of blocks.  The blocks must already be stored in
-// the database, or at least stored into the the passed transaction.  It also
-// tests several error conditions such as ensuring the expected errors are
-// returned when fetching blocks, headers, and regions that don't exist.
-func testFetchBlockIO(tc *testContext, tx database.Tx) bool {
-       // ---------------------
-       // Non-bulk Block IO API
-       // ---------------------
-
-       // Test the individual block APIs one block at a time.  Also, build the
-       // data needed to test the bulk APIs below while looping.
-       allBlockHashes := make([]chainhash.Hash, len(tc.blocks))
-       allBlockBytes := make([][]byte, len(tc.blocks))
-       allBlockTxLocs := make([][]wire.TxLoc, len(tc.blocks))
-       allBlockRegions := make([]database.BlockRegion, len(tc.blocks))
-       for i, block := range tc.blocks {
-               blockHash := block.Hash()
-               allBlockHashes[i] = *blockHash
-
-               blockBytes, err := block.Bytes()
-               if err != nil {
-                       tc.t.Errorf("block.Bytes(%d): unexpected error: %v", i,
-                               err)
-                       return false
-               }
-               allBlockBytes[i] = blockBytes
-
-               txLocs, err := block.TxLoc()
-               if err != nil {
-                       tc.t.Errorf("block.TxLoc(%d): unexpected error: %v", i,
-                               err)
-                       return false
-               }
-               allBlockTxLocs[i] = txLocs
-
-               // Ensure the block data fetched from the database matches the
-               // expected bytes.
-               gotBlockBytes, err := tx.FetchBlock(blockHash)
-               if err != nil {
-                       tc.t.Errorf("FetchBlock(%s): unexpected error: %v",
-                               blockHash, err)
-                       return false
-               }
-               if !bytes.Equal(gotBlockBytes, blockBytes) {
-                       tc.t.Errorf("FetchBlock(%s): bytes mismatch: got %x, "+
-                               "want %x", blockHash, gotBlockBytes, blockBytes)
-                       return false
-               }
-
-               // Ensure the block header fetched from the database matches the
-               // expected bytes.
-               wantHeaderBytes := blockBytes[0:wire.MaxBlockHeaderPayload]
-               gotHeaderBytes, err := tx.FetchBlockHeader(blockHash)
-               if err != nil {
-                       tc.t.Errorf("FetchBlockHeader(%s): unexpected error: %v",
-                               blockHash, err)
-                       return false
-               }
-               if !bytes.Equal(gotHeaderBytes, wantHeaderBytes) {
-                       tc.t.Errorf("FetchBlockHeader(%s): bytes mismatch: "+
-                               "got %x, want %x", blockHash, gotHeaderBytes,
-                               wantHeaderBytes)
-                       return false
-               }
-
-               // Ensure the first transaction fetched as a block region from
-               // the database matches the expected bytes.
-               region := database.BlockRegion{
-                       Hash:   blockHash,
-                       Offset: uint32(txLocs[0].TxStart),
-                       Len:    uint32(txLocs[0].TxLen),
-               }
-               allBlockRegions[i] = region
-               endRegionOffset := region.Offset + region.Len
-               wantRegionBytes := blockBytes[region.Offset:endRegionOffset]
-               gotRegionBytes, err := tx.FetchBlockRegion(&region)
-               if err != nil {
-                       tc.t.Errorf("FetchBlockRegion(%s): unexpected error: %v",
-                               blockHash, err)
-                       return false
-               }
-               if !bytes.Equal(gotRegionBytes, wantRegionBytes) {
-                       tc.t.Errorf("FetchBlockRegion(%s): bytes mismatch: "+
-                               "got %x, want %x", blockHash, gotRegionBytes,
-                               wantRegionBytes)
-                       return false
-               }
-
-               // Ensure the block header fetched from the database matches the
-               // expected bytes.
-               hasBlock, err := tx.HasBlock(blockHash)
-               if err != nil {
-                       tc.t.Errorf("HasBlock(%s): unexpected error: %v",
-                               blockHash, err)
-                       return false
-               }
-               if !hasBlock {
-                       tc.t.Errorf("HasBlock(%s): database claims it doesn't "+
-                               "have the block when it should", blockHash)
-                       return false
-               }
-
-               // -----------------------
-               // Invalid blocks/regions.
-               // -----------------------
-
-               // Ensure fetching a block that doesn't exist returns the
-               // expected error.
-               badBlockHash := &chainhash.Hash{}
-               testName := fmt.Sprintf("FetchBlock(%s) invalid block",
-                       badBlockHash)
-               wantErrCode := database.ErrBlockNotFound
-               _, err = tx.FetchBlock(badBlockHash)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // Ensure fetching a block header that doesn't exist returns
-               // the expected error.
-               testName = fmt.Sprintf("FetchBlockHeader(%s) invalid block",
-                       badBlockHash)
-               _, err = tx.FetchBlockHeader(badBlockHash)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // Ensure fetching a block region in a block that doesn't exist
-               // return the expected error.
-               testName = fmt.Sprintf("FetchBlockRegion(%s) invalid hash",
-                       badBlockHash)
-               wantErrCode = database.ErrBlockNotFound
-               region.Hash = badBlockHash
-               region.Offset = ^uint32(0)
-               _, err = tx.FetchBlockRegion(&region)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // Ensure fetching a block region that is out of bounds returns
-               // the expected error.
-               testName = fmt.Sprintf("FetchBlockRegion(%s) invalid region",
-                       blockHash)
-               wantErrCode = database.ErrBlockRegionInvalid
-               region.Hash = blockHash
-               region.Offset = ^uint32(0)
-               _, err = tx.FetchBlockRegion(&region)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-       }
-
-       // -----------------
-       // Bulk Block IO API
-       // -----------------
-
-       // Ensure the bulk block data fetched from the database matches the
-       // expected bytes.
-       blockData, err := tx.FetchBlocks(allBlockHashes)
-       if err != nil {
-               tc.t.Errorf("FetchBlocks: unexpected error: %v", err)
-               return false
-       }
-       if len(blockData) != len(allBlockBytes) {
-               tc.t.Errorf("FetchBlocks: unexpected number of results - got "+
-                       "%d, want %d", len(blockData), len(allBlockBytes))
-               return false
-       }
-       for i := 0; i < len(blockData); i++ {
-               blockHash := allBlockHashes[i]
-               wantBlockBytes := allBlockBytes[i]
-               gotBlockBytes := blockData[i]
-               if !bytes.Equal(gotBlockBytes, wantBlockBytes) {
-                       tc.t.Errorf("FetchBlocks(%s): bytes mismatch: got %x, "+
-                               "want %x", blockHash, gotBlockBytes,
-                               wantBlockBytes)
-                       return false
-               }
-       }
-
-       // Ensure the bulk block headers fetched from the database match the
-       // expected bytes.
-       blockHeaderData, err := tx.FetchBlockHeaders(allBlockHashes)
-       if err != nil {
-               tc.t.Errorf("FetchBlockHeaders: unexpected error: %v", err)
-               return false
-       }
-       if len(blockHeaderData) != len(allBlockBytes) {
-               tc.t.Errorf("FetchBlockHeaders: unexpected number of results "+
-                       "- got %d, want %d", len(blockHeaderData),
-                       len(allBlockBytes))
-               return false
-       }
-       for i := 0; i < len(blockHeaderData); i++ {
-               blockHash := allBlockHashes[i]
-               wantHeaderBytes := allBlockBytes[i][0:wire.MaxBlockHeaderPayload]
-               gotHeaderBytes := blockHeaderData[i]
-               if !bytes.Equal(gotHeaderBytes, wantHeaderBytes) {
-                       tc.t.Errorf("FetchBlockHeaders(%s): bytes mismatch: "+
-                               "got %x, want %x", blockHash, gotHeaderBytes,
-                               wantHeaderBytes)
-                       return false
-               }
-       }
-
-       // Ensure the first transaction of every block fetched in bulk block
-       // regions from the database matches the expected bytes.
-       allRegionBytes, err := tx.FetchBlockRegions(allBlockRegions)
-       if err != nil {
-               tc.t.Errorf("FetchBlockRegions: unexpected error: %v", err)
-               return false
-
-       }
-       if len(allRegionBytes) != len(allBlockRegions) {
-               tc.t.Errorf("FetchBlockRegions: unexpected number of results "+
-                       "- got %d, want %d", len(allRegionBytes),
-                       len(allBlockRegions))
-               return false
-       }
-       for i, gotRegionBytes := range allRegionBytes {
-               region := &allBlockRegions[i]
-               endRegionOffset := region.Offset + region.Len
-               wantRegionBytes := blockData[i][region.Offset:endRegionOffset]
-               if !bytes.Equal(gotRegionBytes, wantRegionBytes) {
-                       tc.t.Errorf("FetchBlockRegions(%d): bytes mismatch: "+
-                               "got %x, want %x", i, gotRegionBytes,
-                               wantRegionBytes)
-                       return false
-               }
-       }
-
-       // Ensure the bulk determination of whether a set of block hashes are in
-       // the database returns true for all loaded blocks.
-       hasBlocks, err := tx.HasBlocks(allBlockHashes)
-       if err != nil {
-               tc.t.Errorf("HasBlocks: unexpected error: %v", err)
-               return false
-       }
-       for i, hasBlock := range hasBlocks {
-               if !hasBlock {
-                       tc.t.Errorf("HasBlocks(%d): should have block", i)
-                       return false
-               }
-       }
-
-       // -----------------------
-       // Invalid blocks/regions.
-       // -----------------------
-
-       // Ensure fetching blocks for which one doesn't exist returns the
-       // expected error.
-       testName := "FetchBlocks invalid hash"
-       badBlockHashes := make([]chainhash.Hash, len(allBlockHashes)+1)
-       copy(badBlockHashes, allBlockHashes)
-       badBlockHashes[len(badBlockHashes)-1] = chainhash.Hash{}
-       wantErrCode := database.ErrBlockNotFound
-       _, err = tx.FetchBlocks(badBlockHashes)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure fetching block headers for which one doesn't exist returns the
-       // expected error.
-       testName = "FetchBlockHeaders invalid hash"
-       _, err = tx.FetchBlockHeaders(badBlockHashes)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure fetching block regions for which one of blocks doesn't exist
-       // returns expected error.
-       testName = "FetchBlockRegions invalid hash"
-       badBlockRegions := make([]database.BlockRegion, len(allBlockRegions)+1)
-       copy(badBlockRegions, allBlockRegions)
-       badBlockRegions[len(badBlockRegions)-1].Hash = &chainhash.Hash{}
-       wantErrCode = database.ErrBlockNotFound
-       _, err = tx.FetchBlockRegions(badBlockRegions)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure fetching block regions that are out of bounds returns the
-       // expected error.
-       testName = "FetchBlockRegions invalid regions"
-       badBlockRegions = badBlockRegions[:len(badBlockRegions)-1]
-       for i := range badBlockRegions {
-               badBlockRegions[i].Offset = ^uint32(0)
-       }
-       wantErrCode = database.ErrBlockRegionInvalid
-       _, err = tx.FetchBlockRegions(badBlockRegions)
-       return checkDbError(tc.t, testName, err, wantErrCode)
-}
-
-// testBlockIOTxInterface ensures that the block IO interface works as expected
-// for both managed read/write and manual transactions.  This function leaves
-// all of the stored blocks in the database.
-func testBlockIOTxInterface(tc *testContext) bool {
-       // Ensure attempting to store a block with a read-only transaction fails
-       // with the expected error.
-       err := tc.db.View(func(tx database.Tx) error {
-               wantErrCode := database.ErrTxNotWritable
-               for i, block := range tc.blocks {
-                       testName := fmt.Sprintf("StoreBlock(%d) on ro tx", i)
-                       err := tx.StoreBlock(block)
-                       if !checkDbError(tc.t, testName, err, wantErrCode) {
-                               return errSubTestFail
-                       }
-               }
-
-               return nil
-       })
-       if err != nil {
-               if err != errSubTestFail {
-                       tc.t.Errorf("%v", err)
-               }
-               return false
-       }
-
-       // Populate the database with loaded blocks and ensure all of the data
-       // fetching APIs work properly on them within the transaction before a
-       // commit or rollback.  Then, force a rollback so the code below can
-       // ensure none of the data actually gets stored.
-       forceRollbackError := fmt.Errorf("force rollback")
-       err = tc.db.Update(func(tx database.Tx) error {
-               // Store all blocks in the same transaction.
-               for i, block := range tc.blocks {
-                       err := tx.StoreBlock(block)
-                       if err != nil {
-                               tc.t.Errorf("StoreBlock #%d: unexpected error: "+
-                                       "%v", i, err)
-                               return errSubTestFail
-                       }
-               }
-
-               // Ensure attempting to store the same block again, before the
-               // transaction has been committed, returns the expected error.
-               wantErrCode := database.ErrBlockExists
-               for i, block := range tc.blocks {
-                       testName := fmt.Sprintf("duplicate block entry #%d "+
-                               "(before commit)", i)
-                       err := tx.StoreBlock(block)
-                       if !checkDbError(tc.t, testName, err, wantErrCode) {
-                               return errSubTestFail
-                       }
-               }
-
-               // Ensure that all data fetches from the stored blocks before
-               // the transaction has been committed work as expected.
-               if !testFetchBlockIO(tc, tx) {
-                       return errSubTestFail
-               }
-
-               return forceRollbackError
-       })
-       if err != forceRollbackError {
-               if err == errSubTestFail {
-                       return false
-               }
-
-               tc.t.Errorf("Update: inner function error not returned - got "+
-                       "%v, want %v", err, forceRollbackError)
-               return false
-       }
-
-       // Ensure rollback was successful
-       err = tc.db.View(func(tx database.Tx) error {
-               if !testFetchBlockIOMissing(tc, tx) {
-                       return errSubTestFail
-               }
-               return nil
-       })
-       if err != nil {
-               if err != errSubTestFail {
-                       tc.t.Errorf("%v", err)
-               }
-               return false
-       }
-
-       // Populate the database with loaded blocks and ensure all of the data
-       // fetching APIs work properly.
-       err = tc.db.Update(func(tx database.Tx) error {
-               // Store a bunch of blocks in the same transaction.
-               for i, block := range tc.blocks {
-                       err := tx.StoreBlock(block)
-                       if err != nil {
-                               tc.t.Errorf("StoreBlock #%d: unexpected error: "+
-                                       "%v", i, err)
-                               return errSubTestFail
-                       }
-               }
-
-               // Ensure attempting to store the same block again while in the
-               // same transaction, but before it has been committed, returns
-               // the expected error.
-               for i, block := range tc.blocks {
-                       testName := fmt.Sprintf("duplicate block entry #%d "+
-                               "(before commit)", i)
-                       wantErrCode := database.ErrBlockExists
-                       err := tx.StoreBlock(block)
-                       if !checkDbError(tc.t, testName, err, wantErrCode) {
-                               return errSubTestFail
-                       }
-               }
-
-               // Ensure that all data fetches from the stored blocks before
-               // the transaction has been committed work as expected.
-               if !testFetchBlockIO(tc, tx) {
-                       return errSubTestFail
-               }
-
-               return nil
-       })
-       if err != nil {
-               if err != errSubTestFail {
-                       tc.t.Errorf("%v", err)
-               }
-               return false
-       }
-
-       // Ensure all data fetch tests work as expected using a managed
-       // read-only transaction after the data was successfully committed
-       // above.
-       err = tc.db.View(func(tx database.Tx) error {
-               if !testFetchBlockIO(tc, tx) {
-                       return errSubTestFail
-               }
-
-               return nil
-       })
-       if err != nil {
-               if err != errSubTestFail {
-                       tc.t.Errorf("%v", err)
-               }
-               return false
-       }
-
-       // Ensure all data fetch tests work as expected using a managed
-       // read-write transaction after the data was successfully committed
-       // above.
-       err = tc.db.Update(func(tx database.Tx) error {
-               if !testFetchBlockIO(tc, tx) {
-                       return errSubTestFail
-               }
-
-               // Ensure attempting to store existing blocks again returns the
-               // expected error.  Note that this is different from the
-               // previous version since this is a new transaction after the
-               // blocks have been committed.
-               wantErrCode := database.ErrBlockExists
-               for i, block := range tc.blocks {
-                       testName := fmt.Sprintf("duplicate block entry #%d "+
-                               "(before commit)", i)
-                       err := tx.StoreBlock(block)
-                       if !checkDbError(tc.t, testName, err, wantErrCode) {
-                               return errSubTestFail
-                       }
-               }
-
-               return nil
-       })
-       if err != nil {
-               if err != errSubTestFail {
-                       tc.t.Errorf("%v", err)
-               }
-               return false
-       }
-
-       return true
-}
-
-// testClosedTxInterface ensures that both the metadata and block IO API
-// functions behave as expected when attempted against a closed transaction.
-func testClosedTxInterface(tc *testContext, tx database.Tx) bool {
-       wantErrCode := database.ErrTxClosed
-       bucket := tx.Metadata()
-       cursor := tx.Metadata().Cursor()
-       bucketName := []byte("closedtxbucket")
-       keyName := []byte("closedtxkey")
-
-       // ------------
-       // Metadata API
-       // ------------
-
-       // Ensure that attempting to get an existing bucket returns nil when the
-       // transaction is closed.
-       if b := bucket.Bucket(bucketName); b != nil {
-               tc.t.Errorf("Bucket: did not return nil on closed tx")
-               return false
-       }
-
-       // Ensure CreateBucket returns expected error.
-       testName := "CreateBucket on closed tx"
-       _, err := bucket.CreateBucket(bucketName)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure CreateBucketIfNotExists returns expected error.
-       testName = "CreateBucketIfNotExists on closed tx"
-       _, err = bucket.CreateBucketIfNotExists(bucketName)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure Delete returns expected error.
-       testName = "Delete on closed tx"
-       err = bucket.Delete(keyName)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure DeleteBucket returns expected error.
-       testName = "DeleteBucket on closed tx"
-       err = bucket.DeleteBucket(bucketName)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure ForEach returns expected error.
-       testName = "ForEach on closed tx"
-       err = bucket.ForEach(nil)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure ForEachBucket returns expected error.
-       testName = "ForEachBucket on closed tx"
-       err = bucket.ForEachBucket(nil)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure Get returns expected error.
-       testName = "Get on closed tx"
-       if k := bucket.Get(keyName); k != nil {
-               tc.t.Errorf("Get: did not return nil on closed tx")
-               return false
-       }
-
-       // Ensure Put returns expected error.
-       testName = "Put on closed tx"
-       err = bucket.Put(keyName, []byte("test"))
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // -------------------
-       // Metadata Cursor API
-       // -------------------
-
-       // Ensure attempting to get a bucket from a cursor on a closed tx gives
-       // back nil.
-       if b := cursor.Bucket(); b != nil {
-               tc.t.Error("Cursor.Bucket: returned non-nil on closed tx")
-               return false
-       }
-
-       // Ensure Cursor.Delete returns expected error.
-       testName = "Cursor.Delete on closed tx"
-       err = cursor.Delete()
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure Cursor.First on a closed tx returns false and nil key/value.
-       if cursor.First() {
-               tc.t.Error("Cursor.First: claims ok on closed tx")
-               return false
-       }
-       if cursor.Key() != nil || cursor.Value() != nil {
-               tc.t.Error("Cursor.First: key and/or value are not nil on " +
-                       "closed tx")
-               return false
-       }
-
-       // Ensure Cursor.Last on a closed tx returns false and nil key/value.
-       if cursor.Last() {
-               tc.t.Error("Cursor.Last: claims ok on closed tx")
-               return false
-       }
-       if cursor.Key() != nil || cursor.Value() != nil {
-               tc.t.Error("Cursor.Last: key and/or value are not nil on " +
-                       "closed tx")
-               return false
-       }
-
-       // Ensure Cursor.Next on a closed tx returns false and nil key/value.
-       if cursor.Next() {
-               tc.t.Error("Cursor.Next: claims ok on closed tx")
-               return false
-       }
-       if cursor.Key() != nil || cursor.Value() != nil {
-               tc.t.Error("Cursor.Next: key and/or value are not nil on " +
-                       "closed tx")
-               return false
-       }
-
-       // Ensure Cursor.Prev on a closed tx returns false and nil key/value.
-       if cursor.Prev() {
-               tc.t.Error("Cursor.Prev: claims ok on closed tx")
-               return false
-       }
-       if cursor.Key() != nil || cursor.Value() != nil {
-               tc.t.Error("Cursor.Prev: key and/or value are not nil on " +
-                       "closed tx")
-               return false
-       }
-
-       // Ensure Cursor.Seek on a closed tx returns false and nil key/value.
-       if cursor.Seek([]byte{}) {
-               tc.t.Error("Cursor.Seek: claims ok on closed tx")
-               return false
-       }
-       if cursor.Key() != nil || cursor.Value() != nil {
-               tc.t.Error("Cursor.Seek: key and/or value are not nil on " +
-                       "closed tx")
-               return false
-       }
-
-       // ---------------------
-       // Non-bulk Block IO API
-       // ---------------------
-
-       // Test the individual block APIs one block at a time to ensure they
-       // return the expected error.  Also, build the data needed to test the
-       // bulk APIs below while looping.
-       allBlockHashes := make([]chainhash.Hash, len(tc.blocks))
-       allBlockRegions := make([]database.BlockRegion, len(tc.blocks))
-       for i, block := range tc.blocks {
-               blockHash := block.Hash()
-               allBlockHashes[i] = *blockHash
-
-               txLocs, err := block.TxLoc()
-               if err != nil {
-                       tc.t.Errorf("block.TxLoc(%d): unexpected error: %v", i,
-                               err)
-                       return false
-               }
-
-               // Ensure StoreBlock returns expected error.
-               testName = "StoreBlock on closed tx"
-               err = tx.StoreBlock(block)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // Ensure FetchBlock returns expected error.
-               testName = fmt.Sprintf("FetchBlock #%d on closed tx", i)
-               _, err = tx.FetchBlock(blockHash)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // Ensure FetchBlockHeader returns expected error.
-               testName = fmt.Sprintf("FetchBlockHeader #%d on closed tx", i)
-               _, err = tx.FetchBlockHeader(blockHash)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // Ensure the first transaction fetched as a block region from
-               // the database returns the expected error.
-               region := database.BlockRegion{
-                       Hash:   blockHash,
-                       Offset: uint32(txLocs[0].TxStart),
-                       Len:    uint32(txLocs[0].TxLen),
-               }
-               allBlockRegions[i] = region
-               _, err = tx.FetchBlockRegion(&region)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-
-               // Ensure HasBlock returns expected error.
-               testName = fmt.Sprintf("HasBlock #%d on closed tx", i)
-               _, err = tx.HasBlock(blockHash)
-               if !checkDbError(tc.t, testName, err, wantErrCode) {
-                       return false
-               }
-       }
-
-       // -----------------
-       // Bulk Block IO API
-       // -----------------
-
-       // Ensure FetchBlocks returns expected error.
-       testName = "FetchBlocks on closed tx"
-       _, err = tx.FetchBlocks(allBlockHashes)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure FetchBlockHeaders returns expected error.
-       testName = "FetchBlockHeaders on closed tx"
-       _, err = tx.FetchBlockHeaders(allBlockHashes)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure FetchBlockRegions returns expected error.
-       testName = "FetchBlockRegions on closed tx"
-       _, err = tx.FetchBlockRegions(allBlockRegions)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // Ensure HasBlocks returns expected error.
-       testName = "HasBlocks on closed tx"
-       _, err = tx.HasBlocks(allBlockHashes)
-       if !checkDbError(tc.t, testName, err, wantErrCode) {
-               return false
-       }
-
-       // ---------------
-       // Commit/Rollback
-       // ---------------
-
-       // Ensure that attempting to rollback or commit a transaction that is
-       // already closed returns the expected error.
-       err = tx.Rollback()
-       if !checkDbError(tc.t, "closed tx rollback", err, wantErrCode) {
-               return false
-       }
-       err = tx.Commit()
-       return checkDbError(tc.t, "closed tx commit", err, wantErrCode)
-}
-
-// testTxClosed ensures that both the metadata and block IO API functions behave
-// as expected when attempted against both read-only and read-write
-// transactions.
-func testTxClosed(tc *testContext) bool {
-       bucketName := []byte("closedtxbucket")
-       keyName := []byte("closedtxkey")
-
-       // Start a transaction, create a bucket and key used for testing, and
-       // immediately perform a commit on it so it is closed.
-       tx, err := tc.db.Begin(true)
-       if err != nil {
-               tc.t.Errorf("Begin(true): unexpected error: %v", err)
-               return false
-       }
-       defer rollbackOnPanic(tc.t, tx)
-       if _, err := tx.Metadata().CreateBucket(bucketName); err != nil {
-               tc.t.Errorf("CreateBucket: unexpected error: %v", err)
-               return false
-       }
-       if err := tx.Metadata().Put(keyName, []byte("test")); err != nil {
-               tc.t.Errorf("Put: unexpected error: %v", err)
-               return false
-       }
-       if err := tx.Commit(); err != nil {
-               tc.t.Errorf("Commit: unexpected error: %v", err)
-               return false
-       }
-
-       // Ensure invoking all of the functions on the closed read-write
-       // transaction behave as expected.
-       if !testClosedTxInterface(tc, tx) {
-               return false
-       }
-
-       // Repeat the tests with a rolled-back read-only transaction.
-       tx, err = tc.db.Begin(false)
-       if err != nil {
-               tc.t.Errorf("Begin(false): unexpected error: %v", err)
-               return false
-       }
-       defer rollbackOnPanic(tc.t, tx)
-       if err := tx.Rollback(); err != nil {
-               tc.t.Errorf("Rollback: unexpected error: %v", err)
-               return false
-       }
-
-       // Ensure invoking all of the functions on the closed read-only
-       // transaction behave as expected.
-       return testClosedTxInterface(tc, tx)
-}
-
-// testConcurrecy ensure the database properly supports concurrent readers and
-// only a single writer.  It also ensures views act as snapshots at the time
-// they are acquired.
-func testConcurrecy(tc *testContext) bool {
-       // sleepTime is how long each of the concurrent readers should sleep to
-       // aid in detection of whether or not the data is actually being read
-       // concurrently.  It starts with a sane lower bound.
-       var sleepTime = time.Millisecond * 250
-
-       // Determine about how long it takes for a single block read.  When it's
-       // longer than the default minimum sleep time, adjust the sleep time to
-       // help prevent durations that are too short which would cause erroneous
-       // test failures on slower systems.
-       startTime := time.Now()
-       err := tc.db.View(func(tx database.Tx) error {
-               _, err := tx.FetchBlock(tc.blocks[0].Hash())
-               return err
-       })
-       if err != nil {
-               tc.t.Errorf("Unexpected error in view: %v", err)
-               return false
-       }
-       elapsed := time.Since(startTime)
-       if sleepTime < elapsed {
-               sleepTime = elapsed
-       }
-       tc.t.Logf("Time to load block 0: %v, using sleep time: %v", elapsed,
-               sleepTime)
-
-       // reader takes a block number to load and channel to return the result
-       // of the operation on.  It is used below to launch multiple concurrent
-       // readers.
-       numReaders := len(tc.blocks)
-       resultChan := make(chan bool, numReaders)
-       reader := func(blockNum int) {
-               err := tc.db.View(func(tx database.Tx) error {
-                       time.Sleep(sleepTime)
-                       _, err := tx.FetchBlock(tc.blocks[blockNum].Hash())
-                       return err
-               })
-               if err != nil {
-                       tc.t.Errorf("Unexpected error in concurrent view: %v",
-                               err)
-                       resultChan <- false
-               }
-               resultChan <- true
-       }
-
-       // Start up several concurrent readers for the same block and wait for
-       // the results.
-       startTime = time.Now()
-       for i := 0; i < numReaders; i++ {
-               go reader(0)
-       }
-       for i := 0; i < numReaders; i++ {
-               if result := <-resultChan; !result {
-                       return false
-               }
-       }
-       elapsed = time.Since(startTime)
-       tc.t.Logf("%d concurrent reads of same block elapsed: %v", numReaders,
-               elapsed)
-
-       // Consider it a failure if it took longer than half the time it would
-       // take with no concurrency.
-       if elapsed > sleepTime*time.Duration(numReaders/2) {
-               tc.t.Errorf("Concurrent views for same block did not appear to "+
-                       "run simultaneously: elapsed %v", elapsed)
-               return false
-       }
-
-       // Start up several concurrent readers for different blocks and wait for
-       // the results.
-       startTime = time.Now()
-       for i := 0; i < numReaders; i++ {
-               go reader(i)
-       }
-       for i := 0; i < numReaders; i++ {
-               if result := <-resultChan; !result {
-                       return false
-               }
-       }
-       elapsed = time.Since(startTime)
-       tc.t.Logf("%d concurrent reads of different blocks elapsed: %v",
-               numReaders, elapsed)
-
-       // Consider it a failure if it took longer than half the time it would
-       // take with no concurrency.
-       if elapsed > sleepTime*time.Duration(numReaders/2) {
-               tc.t.Errorf("Concurrent views for different blocks did not "+
-                       "appear to run simultaneously: elapsed %v", elapsed)
-               return false
-       }
-
-       // Start up a few readers and wait for them to acquire views.  Each
-       // reader waits for a signal from the writer to be finished to ensure
-       // that the data written by the writer is not seen by the view since it
-       // was started before the data was set.
-       concurrentKey := []byte("notthere")
-       concurrentVal := []byte("someval")
-       started := make(chan struct{})
-       writeComplete := make(chan struct{})
-       reader = func(blockNum int) {
-               err := tc.db.View(func(tx database.Tx) error {
-                       started <- struct{}{}
-
-                       // Wait for the writer to complete.
-                       <-writeComplete
-
-                       // Since this reader was created before the write took
-                       // place, the data it added should not be visible.
-                       val := tx.Metadata().Get(concurrentKey)
-                       if val != nil {
-                               return fmt.Errorf("%s should not be visible",
-                                       concurrentKey)
-                       }
-                       return nil
-               })
-               if err != nil {
-                       tc.t.Errorf("Unexpected error in concurrent view: %v",
-                               err)
-                       resultChan <- false
-               }
-               resultChan <- true
-       }
-       for i := 0; i < numReaders; i++ {
-               go reader(0)
-       }
-       for i := 0; i < numReaders; i++ {
-               <-started
-       }
-
-       // All readers are started and waiting for completion of the writer.
-       // Set some data the readers are expecting to not find and signal the
-       // readers the write is done by closing the writeComplete channel.
-       err = tc.db.Update(func(tx database.Tx) error {
-               return tx.Metadata().Put(concurrentKey, concurrentVal)
-       })
-       if err != nil {
-               tc.t.Errorf("Unexpected error in update: %v", err)
-               return false
-       }
-       close(writeComplete)
-
-       // Wait for reader results.
-       for i := 0; i < numReaders; i++ {
-               if result := <-resultChan; !result {
-                       return false
-               }
-       }
-
-       // Start a few writers and ensure the total time is at least the
-       // writeSleepTime * numWriters.  This ensures only one write transaction
-       // can be active at a time.
-       writeSleepTime := time.Millisecond * 250
-       writer := func() {
-               err := tc.db.Update(func(tx database.Tx) error {
-                       time.Sleep(writeSleepTime)
-                       return nil
-               })
-               if err != nil {
-                       tc.t.Errorf("Unexpected error in concurrent view: %v",
-                               err)
-                       resultChan <- false
-               }
-               resultChan <- true
-       }
-       numWriters := 3
-       startTime = time.Now()
-       for i := 0; i < numWriters; i++ {
-               go writer()
-       }
-       for i := 0; i < numWriters; i++ {
-               if result := <-resultChan; !result {
-                       return false
-               }
-       }
-       elapsed = time.Since(startTime)
-       tc.t.Logf("%d concurrent writers elapsed using sleep time %v: %v",
-               numWriters, writeSleepTime, elapsed)
-
-       // The total time must have been at least the sum of all sleeps if the
-       // writes blocked properly.
-       if elapsed < writeSleepTime*time.Duration(numWriters) {
-               tc.t.Errorf("Concurrent writes appeared to run simultaneously: "+
-                       "elapsed %v", elapsed)
-               return false
-       }
-
-       return true
-}
-
-// testConcurrentClose ensures that closing the database with open transactions
-// blocks until the transactions are finished.
-//
-// The database will be closed upon returning from this function.
-func testConcurrentClose(tc *testContext) bool {
-       // Start up a few readers and wait for them to acquire views.  Each
-       // reader waits for a signal to complete to ensure the transactions stay
-       // open until they are explicitly signalled to be closed.
-       var activeReaders int32
-       numReaders := 3
-       started := make(chan struct{})
-       finishReaders := make(chan struct{})
-       resultChan := make(chan bool, numReaders+1)
-       reader := func() {
-               err := tc.db.View(func(tx database.Tx) error {
-                       atomic.AddInt32(&activeReaders, 1)
-                       started <- struct{}{}
-                       <-finishReaders
-                       atomic.AddInt32(&activeReaders, -1)
-                       return nil
-               })
-               if err != nil {
-                       tc.t.Errorf("Unexpected error in concurrent view: %v",
-                               err)
-                       resultChan <- false
-               }
-               resultChan <- true
-       }
-       for i := 0; i < numReaders; i++ {
-               go reader()
-       }
-       for i := 0; i < numReaders; i++ {
-               <-started
-       }
-
-       // Close the database in a separate goroutine.  This should block until
-       // the transactions are finished.  Once the close has taken place, the
-       // dbClosed channel is closed to signal the main goroutine below.
-       dbClosed := make(chan struct{})
-       go func() {
-               started <- struct{}{}
-               err := tc.db.Close()
-               if err != nil {
-                       tc.t.Errorf("Unexpected error in concurrent view: %v",
-                               err)
-                       resultChan <- false
-               }
-               close(dbClosed)
-               resultChan <- true
-       }()
-       <-started
-
-       // Wait a short period and then signal the reader transactions to
-       // finish.  When the db closed channel is received, ensure there are no
-       // active readers open.
-       time.AfterFunc(time.Millisecond*250, func() { close(finishReaders) })
-       <-dbClosed
-       if nr := atomic.LoadInt32(&activeReaders); nr != 0 {
-               tc.t.Errorf("Close did not appear to block with active "+
-                       "readers: %d active", nr)
-               return false
-       }
-
-       // Wait for all results.
-       for i := 0; i < numReaders+1; i++ {
-               if result := <-resultChan; !result {
-                       return false
-               }
-       }
-
-       return true
-}
-
-// testInterface tests performs tests for the various interfaces of the database
-// package which require state in the database for the given database type.
-func testInterface(t *testing.T, db database.DB) {
-       // Create a test context to pass around.
-       context := testContext{t: t, db: db}
-
-       // Load the test blocks and store in the test context for use throughout
-       // the tests.
-       blocks, err := loadBlocks(t, blockDataFile, blockDataNet)
-       if err != nil {
-               t.Errorf("loadBlocks: Unexpected error: %v", err)
-               return
-       }
-       context.blocks = blocks
-
-       // Test the transaction metadata interface including managed and manual
-       // transactions as well as buckets.
-       if !testMetadataTxInterface(&context) {
-               return
-       }
-
-       // Test the transaction block IO interface using managed and manual
-       // transactions.  This function leaves all of the stored blocks in the
-       // database since they're used later.
-       if !testBlockIOTxInterface(&context) {
-               return
-       }
-
-       // Test all of the transaction interface functions against a closed
-       // transaction work as expected.
-       if !testTxClosed(&context) {
-               return
-       }
-
-       // Test the database properly supports concurrency.
-       if !testConcurrecy(&context) {
-               return
-       }
-
-       // Test that closing the database with open transactions blocks until
-       // the transactions are finished.
-       //
-       // The database will be closed upon returning from this function, so it
-       // must be the last thing called.
-       testConcurrentClose(&context)
-}