Add errors to all interface methods.

This commit adds error returns to all of the Db interface methods except
for FetchTxByShaList and FetchUnSpentTxByShaList since they expose the
errors on each individual transaction.

It also updates all tests and code for both the leveldb and memdb drivers
for the changes.

Closes #5.

ok @drahn
This commit is contained in:
Dave Collins 2014-07-07 09:50:50 -05:00
parent d31183ff19
commit 16dc2cf2d0
11 changed files with 147 additions and 75 deletions

20
db.go
View file

@ -30,7 +30,7 @@ const AllShas = int64(^uint64(0) >> 1)
// used to add a new backend data storage method. // used to add a new backend data storage method.
type Db interface { type Db interface {
// Close cleanly shuts down the database and syncs all data. // Close cleanly shuts down the database and syncs all data.
Close() Close() (err error)
// DropAfterBlockBySha will remove any blocks from the database after // DropAfterBlockBySha will remove any blocks from the database after
// the given block. It terminates any existing transaction and performs // the given block. It terminates any existing transaction and performs
@ -40,7 +40,7 @@ type Db interface {
// ExistsSha returns whether or not the given block hash is present in // ExistsSha returns whether or not the given block hash is present in
// the database. // the database.
ExistsSha(sha *btcwire.ShaHash) (exists bool) ExistsSha(sha *btcwire.ShaHash) (exists bool, err error)
// FetchBlockBySha returns a btcutil Block. The implementation may // FetchBlockBySha returns a btcutil Block. The implementation may
// cache the underlying data if desired. // cache the underlying data if desired.
@ -65,7 +65,7 @@ type Db interface {
// ExistsTxSha returns whether or not the given tx hash is present in // ExistsTxSha returns whether or not the given tx hash is present in
// the database // the database
ExistsTxSha(sha *btcwire.ShaHash) (exists bool) ExistsTxSha(sha *btcwire.ShaHash) (exists bool, err error)
// FetchTxBySha returns some data for the given transaction hash. The // FetchTxBySha returns some data for the given transaction hash. The
// implementation may cache the underlying data if desired. // implementation may cache the underlying data if desired.
@ -75,12 +75,22 @@ type Db interface {
// hashes. The implementation may cache the underlying data if desired. // hashes. The implementation may cache the underlying data if desired.
// This differs from FetchUnSpentTxByShaList in that it will return // This differs from FetchUnSpentTxByShaList in that it will return
// the most recent known Tx, if it is fully spent or not. // the most recent known Tx, if it is fully spent or not.
//
// NOTE: This function does not return an error directly since it MUST
// return at least one TxListReply instance for each requested
// transaction. Each TxListReply instance then contains an Err field
// which can be used to detect errors.
FetchTxByShaList(txShaList []*btcwire.ShaHash) []*TxListReply FetchTxByShaList(txShaList []*btcwire.ShaHash) []*TxListReply
// FetchUnSpentTxByShaList returns a TxListReply given an array of // FetchUnSpentTxByShaList returns a TxListReply given an array of
// transaction hashes. The implementation may cache the underlying // transaction hashes. The implementation may cache the underlying
// data if desired. Fully spent transactions will not normally not // data if desired. Fully spent transactions will not normally not
// be returned in this operation. // be returned in this operation.
//
// NOTE: This function does not return an error directly since it MUST
// return at least one TxListReply instance for each requested
// transaction. Each TxListReply instance then contains an Err field
// which can be used to detect errors.
FetchUnSpentTxByShaList(txShaList []*btcwire.ShaHash) []*TxListReply FetchUnSpentTxByShaList(txShaList []*btcwire.ShaHash) []*TxListReply
// InsertBlock inserts raw block and transaction data from a block // InsertBlock inserts raw block and transaction data from a block
@ -97,11 +107,11 @@ type Db interface {
// RollbackClose discards the recent database changes to the previously // RollbackClose discards the recent database changes to the previously
// saved data at last Sync and closes the database. // saved data at last Sync and closes the database.
RollbackClose() RollbackClose() (err error)
// Sync verifies that the database is coherent on disk and no // Sync verifies that the database is coherent on disk and no
// outstanding transactions are in flight. // outstanding transactions are in flight.
Sync() Sync() (err error)
} }
// DriverDB defines a structure for backend drivers to use when they registered // DriverDB defines a structure for backend drivers to use when they registered

View file

@ -85,7 +85,13 @@ func testNewestSha(tc *testContext) bool {
// testExistsSha ensures ExistsSha conforms to the interface contract. // testExistsSha ensures ExistsSha conforms to the interface contract.
func testExistsSha(tc *testContext) bool { func testExistsSha(tc *testContext) bool {
// The block must exist in the database. // The block must exist in the database.
if exists := tc.db.ExistsSha(tc.blockHash); !exists { exists, err := tc.db.ExistsSha(tc.blockHash)
if err != nil {
tc.t.Errorf("ExistsSha (%s): block #%d (%s) unexpected error: "+
"%v", tc.dbType, tc.blockHeight, tc.blockHash, err)
return false
}
if !exists {
tc.t.Errorf("ExistsSha (%s): block #%d (%s) does not exist", tc.t.Errorf("ExistsSha (%s): block #%d (%s) does not exist",
tc.dbType, tc.blockHeight, tc.blockHash) tc.dbType, tc.blockHeight, tc.blockHash)
return false return false
@ -232,7 +238,14 @@ func testExistsTxSha(tc *testContext) bool {
for i, tx := range tc.block.Transactions() { for i, tx := range tc.block.Transactions() {
// The transaction must exist in the database. // The transaction must exist in the database.
txHash := tx.Sha() txHash := tx.Sha()
if exists := tc.db.ExistsTxSha(txHash); !exists { exists, err := tc.db.ExistsTxSha(txHash)
if err != nil {
tc.t.Errorf("ExistsTxSha (%s): block #%d (%s) tx #%d "+
"(%s) unexpected error: %v", tc.dbType,
tc.blockHeight, tc.blockHash, i, txHash, err)
return false
}
if !exists {
_, err := tc.db.FetchTxBySha(txHash) _, err := tc.db.FetchTxBySha(txHash)
if err != nil { if err != nil {
tc.t.Errorf("ExistsTxSha (%s): block #%d (%s) "+ tc.t.Errorf("ExistsTxSha (%s): block #%d (%s) "+

View file

@ -11,6 +11,7 @@ import (
"github.com/conformal/btcdb" "github.com/conformal/btcdb"
"github.com/conformal/btcutil" "github.com/conformal/btcutil"
"github.com/conformal/btcwire" "github.com/conformal/btcwire"
"github.com/conformal/goleveldb/leveldb"
) )
// FetchBlockBySha - return a btcutil Block // FetchBlockBySha - return a btcutil Block
@ -186,30 +187,26 @@ func (db *LevelDb) fetchSha(sha *btcwire.ShaHash) (rbuf []byte,
// ExistsSha looks up the given block hash // ExistsSha looks up the given block hash
// returns true if it is present in the database. // returns true if it is present in the database.
func (db *LevelDb) ExistsSha(sha *btcwire.ShaHash) (exists bool) { func (db *LevelDb) ExistsSha(sha *btcwire.ShaHash) (bool, error) {
db.dbLock.Lock() db.dbLock.Lock()
defer db.dbLock.Unlock() defer db.dbLock.Unlock()
// not in cache, try database // not in cache, try database
exists = db.blkExistsSha(sha) return db.blkExistsSha(sha)
return
} }
// blkExistsSha looks up the given block hash // blkExistsSha looks up the given block hash
// returns true if it is present in the database. // returns true if it is present in the database.
// CALLED WITH LOCK HELD // CALLED WITH LOCK HELD
func (db *LevelDb) blkExistsSha(sha *btcwire.ShaHash) bool { func (db *LevelDb) blkExistsSha(sha *btcwire.ShaHash) (bool, error) {
_, err := db.getBlkLoc(sha) _, err := db.getBlkLoc(sha)
switch err {
if err != nil { case nil:
/* return true, nil
should this warn if the failure is something besides does not exist ? case leveldb.ErrNotFound:
log.Warnf("blkExistsSha: fail %v", err) return false, nil
*/
return false
} }
return true return false, err
} }
// FetchBlockShaByHeight returns a block hash based on its height in the // FetchBlockShaByHeight returns a block hash based on its height in the

View file

@ -37,14 +37,20 @@ func TestEmptyDB(t *testing.T) {
} }
// This is a reopen test // This is a reopen test
db.Close() if err := db.Close(); err != nil {
t.Errorf("Close: unexpected error: %v", err)
}
db, err = btcdb.OpenDB("leveldb", dbname) db, err = btcdb.OpenDB("leveldb", dbname)
if err != nil { if err != nil {
t.Errorf("Failed to open test database %v", err) t.Errorf("Failed to open test database %v", err)
return return
} }
defer db.Close() defer func() {
if err := db.Close(); err != nil {
t.Errorf("Close: unexpected error: %v", err)
}
}()
sha, height, err = db.NewestSha() sha, height, err = db.NewestSha()
if !sha.IsEqual(&btcwire.ShaHash{}) { if !sha.IsEqual(&btcwire.ShaHash{}) {

View file

@ -29,7 +29,11 @@ func Test_dupTx(t *testing.T) {
} }
defer os.RemoveAll(dbname) defer os.RemoveAll(dbname)
defer os.RemoveAll(dbnamever) defer os.RemoveAll(dbnamever)
defer db.Close() defer func() {
if err := db.Close(); err != nil {
t.Errorf("Close: unexpected error: %v", err)
}
}()
testdatafile := filepath.Join("testdata", "blocks1-256.bz2") testdatafile := filepath.Join("testdata", "blocks1-256.bz2")
blocks, err := loadBlocks(t, testdatafile) blocks, err := loadBlocks(t, testdatafile)
@ -58,7 +62,11 @@ out:
origintxsha := &txin.PreviousOutpoint.Hash origintxsha := &txin.PreviousOutpoint.Hash
txneededList = append(txneededList, origintxsha) txneededList = append(txneededList, origintxsha)
if !db.ExistsTxSha(origintxsha) { exists, err := db.ExistsTxSha(origintxsha)
if err != nil {
t.Errorf("ExistsTxSha: unexpected error %v ", err)
}
if !exists {
t.Errorf("referenced tx not found %v ", origintxsha) t.Errorf("referenced tx not found %v ", origintxsha)
} }

View file

@ -55,7 +55,11 @@ func testUnspentInsert(t *testing.T) {
} }
defer os.RemoveAll(dbname) defer os.RemoveAll(dbname)
defer os.RemoveAll(dbnamever) defer os.RemoveAll(dbnamever)
defer db.Close() defer func() {
if err := db.Close(); err != nil {
t.Errorf("Close: unexpected error: %v", err)
}
}()
blocks := loadblocks(t) blocks := loadblocks(t)
endtest: endtest:
@ -79,10 +83,13 @@ endtest:
txneededList = append(txneededList, origintxsha) txneededList = append(txneededList, origintxsha)
txlookupList = append(txlookupList, origintxsha) txlookupList = append(txlookupList, origintxsha)
if !db.ExistsTxSha(origintxsha) { exists, err := db.ExistsTxSha(origintxsha)
if err != nil {
t.Errorf("ExistsTxSha: unexpected error %v ", err)
}
if !exists {
t.Errorf("referenced tx not found %v ", origintxsha) t.Errorf("referenced tx not found %v ", origintxsha)
} }
} }
txshaname, _ := tx.TxSha() txshaname, _ := tx.TxSha()
txlookupList = append(txlookupList, &txshaname) txlookupList = append(txlookupList, &txshaname)

View file

@ -255,26 +255,27 @@ func CreateDB(args ...interface{}) (btcdb.Db, error) {
return db, err return db, err
} }
func (db *LevelDb) close() { func (db *LevelDb) close() error {
db.lDb.Close() return db.lDb.Close()
} }
// Sync verifies that the database is coherent on disk, // Sync verifies that the database is coherent on disk,
// and no outstanding transactions are in flight. // and no outstanding transactions are in flight.
func (db *LevelDb) Sync() { func (db *LevelDb) Sync() error {
db.dbLock.Lock() db.dbLock.Lock()
defer db.dbLock.Unlock() defer db.dbLock.Unlock()
// while specified by the API, does nothing // while specified by the API, does nothing
// however does grab lock to verify it does not return until other operations are complete. // however does grab lock to verify it does not return until other operations are complete.
return nil
} }
// Close cleanly shuts down database, syncing all data. // Close cleanly shuts down database, syncing all data.
func (db *LevelDb) Close() { func (db *LevelDb) Close() error {
db.dbLock.Lock() db.dbLock.Lock()
defer db.dbLock.Unlock() defer db.dbLock.Unlock()
db.close() return db.close()
} }
// DropAfterBlockBySha will remove any blocks from the database after // DropAfterBlockBySha will remove any blocks from the database after
@ -682,9 +683,9 @@ func (db *LevelDb) processBatches() error {
return nil return nil
} }
func (db *LevelDb) RollbackClose() { func (db *LevelDb) RollbackClose() error {
db.dbLock.Lock() db.dbLock.Lock()
defer db.dbLock.Unlock() defer db.dbLock.Unlock()
db.close() return db.close()
} }

View file

@ -44,7 +44,11 @@ func testOperationalMode(t *testing.T) {
} }
defer os.RemoveAll(dbname) defer os.RemoveAll(dbname)
defer os.RemoveAll(dbnamever) defer os.RemoveAll(dbnamever)
defer db.Close() defer func() {
if err := db.Close(); err != nil {
t.Errorf("Close: unexpected error: %v", err)
}
}()
testdatafile := filepath.Join("..", "testdata", "blocks1-256.bz2") testdatafile := filepath.Join("..", "testdata", "blocks1-256.bz2")
blocks, err := loadBlocks(t, testdatafile) blocks, err := loadBlocks(t, testdatafile)
@ -67,9 +71,14 @@ out:
origintxsha := &txin.PreviousOutpoint.Hash origintxsha := &txin.PreviousOutpoint.Hash
txneededList = append(txneededList, origintxsha) txneededList = append(txneededList, origintxsha)
if !db.ExistsTxSha(origintxsha) { exists, err := db.ExistsTxSha(origintxsha)
if err != nil {
t.Errorf("ExistsTxSha: unexpected error %v ", err)
}
if !exists {
t.Errorf("referenced tx not found %v ", origintxsha) t.Errorf("referenced tx not found %v ", origintxsha)
} }
_, err = db.FetchTxBySha(origintxsha) _, err = db.FetchTxBySha(origintxsha)
if err != nil { if err != nil {
t.Errorf("referenced tx not found %v err %v ", origintxsha, err) t.Errorf("referenced tx not found %v err %v ", origintxsha, err)
@ -178,14 +187,20 @@ func testBackout(t *testing.T) {
t.Errorf("Failed to open test database %v", err) t.Errorf("Failed to open test database %v", err)
return return
} }
defer db.Close() defer func() {
if err := db.Close(); err != nil {
t.Errorf("Close: unexpected error: %v", err)
}
}()
sha, err := blocks[99].Sha() sha, err := blocks[99].Sha()
if err != nil { if err != nil {
t.Errorf("failed to get block 99 sha err %v", err) t.Errorf("failed to get block 99 sha err %v", err)
return return
} }
_ = db.ExistsSha(sha) if _, err := db.ExistsSha(sha); err != nil {
t.Errorf("ExistsSha: unexpected error: %v")
}
_, err = db.FetchBlockBySha(sha) _, err = db.FetchBlockBySha(sha)
if err != nil { if err != nil {
t.Errorf("failed to load block 99 from db %v", err) t.Errorf("failed to load block 99 from db %v", err)
@ -197,7 +212,9 @@ func testBackout(t *testing.T) {
t.Errorf("failed to get block 110 sha err %v", err) t.Errorf("failed to get block 110 sha err %v", err)
return return
} }
_ = db.ExistsSha(sha) if _, err := db.ExistsSha(sha); err != nil {
t.Errorf("ExistsSha: unexpected error: %v")
}
_, err = db.FetchBlockBySha(sha) _, err = db.FetchBlockBySha(sha)
if err != nil { if err != nil {
t.Errorf("loaded block 119 from db") t.Errorf("loaded block 119 from db")
@ -207,7 +224,10 @@ func testBackout(t *testing.T) {
block := blocks[119] block := blocks[119]
mblock := block.MsgBlock() mblock := block.MsgBlock()
txsha, err := mblock.Transactions[0].TxSha() txsha, err := mblock.Transactions[0].TxSha()
exists := db.ExistsTxSha(&txsha) exists, err := db.ExistsTxSha(&txsha)
if err != nil {
t.Errorf("ExistsTxSha: unexpected error %v ", err)
}
if !exists { if !exists {
t.Errorf("tx %v not located db\n", txsha) t.Errorf("tx %v not located db\n", txsha)
} }

View file

@ -147,7 +147,7 @@ func (db *LevelDb) formatTxFullySpent(sTxList []*spentTx) []byte {
} }
// ExistsTxSha returns if the given tx sha exists in the database // ExistsTxSha returns if the given tx sha exists in the database
func (db *LevelDb) ExistsTxSha(txsha *btcwire.ShaHash) (exists bool) { func (db *LevelDb) ExistsTxSha(txsha *btcwire.ShaHash) (bool, error) {
db.dbLock.Lock() db.dbLock.Lock()
defer db.dbLock.Unlock() defer db.dbLock.Unlock()
@ -156,15 +156,15 @@ func (db *LevelDb) ExistsTxSha(txsha *btcwire.ShaHash) (exists bool) {
// existsTxSha returns if the given tx sha exists in the database.o // existsTxSha returns if the given tx sha exists in the database.o
// Must be called with the db lock held. // Must be called with the db lock held.
func (db *LevelDb) existsTxSha(txSha *btcwire.ShaHash) (exists bool) { func (db *LevelDb) existsTxSha(txSha *btcwire.ShaHash) (bool, error) {
_, _, _, _, err := db.getTxData(txSha) _, _, _, _, err := db.getTxData(txSha)
if err == nil { switch err {
return true case nil:
return true, nil
case leveldb.ErrNotFound:
return false, nil
} }
return false, err
// BUG(drahn) If there was an error beside non-existant deal with it.
return false
} }
// FetchTxByShaList returns the most recent tx of the name fully spent or not // FetchTxByShaList returns the most recent tx of the name fully spent or not

View file

@ -138,14 +138,19 @@ func (db *MemDb) removeTx(msgTx *btcwire.MsgTx, txHash *btcwire.ShaHash) {
// //
// All data is purged upon close with this implementation since it is a // All data is purged upon close with this implementation since it is a
// memory-only database. // memory-only database.
func (db *MemDb) Close() { func (db *MemDb) Close() error {
db.Lock() db.Lock()
defer db.Unlock() defer db.Unlock()
if db.closed {
return ErrDbClosed
}
db.blocks = nil db.blocks = nil
db.blocksBySha = nil db.blocksBySha = nil
db.txns = nil db.txns = nil
db.closed = true db.closed = true
return nil
} }
// DropAfterBlockBySha removes any blocks from the database after the given // DropAfterBlockBySha removes any blocks from the database after the given
@ -192,20 +197,19 @@ func (db *MemDb) DropAfterBlockBySha(sha *btcwire.ShaHash) error {
// ExistsSha returns whether or not the given block hash is present in the // ExistsSha returns whether or not the given block hash is present in the
// database. This is part of the btcdb.Db interface implementation. // database. This is part of the btcdb.Db interface implementation.
func (db *MemDb) ExistsSha(sha *btcwire.ShaHash) bool { func (db *MemDb) ExistsSha(sha *btcwire.ShaHash) (bool, error) {
db.Lock() db.Lock()
defer db.Unlock() defer db.Unlock()
if db.closed { if db.closed {
log.Warnf("ExistsSha called after db close.") return false, ErrDbClosed
return false
} }
if _, exists := db.blocksBySha[*sha]; exists { if _, exists := db.blocksBySha[*sha]; exists {
return true return true, nil
} }
return false return false, nil
} }
// FetchBlockBySha returns a btcutil.Block. The implementation may cache the // FetchBlockBySha returns a btcutil.Block. The implementation may cache the
@ -346,20 +350,19 @@ func (db *MemDb) FetchHeightRange(startHeight, endHeight int64) ([]btcwire.ShaHa
// ExistsTxSha returns whether or not the given transaction hash is present in // ExistsTxSha returns whether or not the given transaction hash is present in
// the database and is not fully spent. This is part of the btcdb.Db interface // the database and is not fully spent. This is part of the btcdb.Db interface
// implementation. // implementation.
func (db *MemDb) ExistsTxSha(sha *btcwire.ShaHash) bool { func (db *MemDb) ExistsTxSha(sha *btcwire.ShaHash) (bool, error) {
db.Lock() db.Lock()
defer db.Unlock() defer db.Unlock()
if db.closed { if db.closed {
log.Warnf("ExistsTxSha called after db close.") return false, ErrDbClosed
return false
} }
if txns, exists := db.txns[*sha]; exists { if txns, exists := db.txns[*sha]; exists {
return !isFullySpent(txns[len(txns)-1]) return !isFullySpent(txns[len(txns)-1]), nil
} }
return false return false, nil
} }
// FetchTxBySha returns some data for the given transaction hash. The // FetchTxBySha returns some data for the given transaction hash. The
@ -702,10 +705,10 @@ func (db *MemDb) NewestSha() (*btcwire.ShaHash, int64, error) {
// The database is completely purged on close with this implementation since the // The database is completely purged on close with this implementation since the
// entire database is only in memory. As a result, this function behaves no // entire database is only in memory. As a result, this function behaves no
// differently than Close. // differently than Close.
func (db *MemDb) RollbackClose() { func (db *MemDb) RollbackClose() error {
// Rollback doesn't apply to a memory database, so just call Close. // Rollback doesn't apply to a memory database, so just call Close.
// Close handles the mutex locks. // Close handles the mutex locks.
db.Close() return db.Close()
} }
// Sync verifies that the database is coherent on disk and no outstanding // Sync verifies that the database is coherent on disk and no outstanding
@ -714,18 +717,18 @@ func (db *MemDb) RollbackClose() {
// //
// This implementation does not write any data to disk, so this function only // This implementation does not write any data to disk, so this function only
// grabs a lock to ensure it doesn't return until other operations are complete. // grabs a lock to ensure it doesn't return until other operations are complete.
func (db *MemDb) Sync() { func (db *MemDb) Sync() error {
db.Lock() db.Lock()
defer db.Unlock() defer db.Unlock()
if db.closed { if db.closed {
log.Warnf("Sync called after db close.") return ErrDbClosed
} }
// There is nothing extra to do to sync the memory database. However, // There is nothing extra to do to sync the memory database. However,
// the lock is still grabbed to ensure the function does not return // the lock is still grabbed to ensure the function does not return
// until other operations are complete. // until other operations are complete.
return return nil
} }
// newMemDb returns a new memory-only database ready for block inserts. // newMemDb returns a new memory-only database ready for block inserts.

View file

@ -29,15 +29,17 @@ func TestClosed(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("InsertBlock: %v", err) t.Errorf("InsertBlock: %v", err)
} }
db.Close() if err := db.Close(); err != nil {
t.Errorf("Close: unexpected error %v", err)
}
genesisHash := btcnet.MainNetParams.GenesisHash genesisHash := btcnet.MainNetParams.GenesisHash
if err := db.DropAfterBlockBySha(genesisHash); err != memdb.ErrDbClosed { if err := db.DropAfterBlockBySha(genesisHash); err != memdb.ErrDbClosed {
t.Errorf("DropAfterBlockBySha: unexpected error %v", err) t.Errorf("DropAfterBlockBySha: unexpected error %v", err)
} }
if exists := db.ExistsSha(genesisHash); exists != false { if _, err := db.ExistsSha(genesisHash); err != memdb.ErrDbClosed {
t.Errorf("ExistsSha: genesis hash exists after close") t.Errorf("ExistsSha: Unexpected error: %v", err)
} }
if _, err := db.FetchBlockBySha(genesisHash); err != memdb.ErrDbClosed { if _, err := db.FetchBlockBySha(genesisHash); err != memdb.ErrDbClosed {
@ -57,9 +59,8 @@ func TestClosed(t *testing.T) {
if err != nil { if err != nil {
t.Errorf("TxSha: unexpected error %v", err) t.Errorf("TxSha: unexpected error %v", err)
} }
if exists := db.ExistsTxSha(&coinbaseHash); exists != false { if _, err := db.ExistsTxSha(&coinbaseHash); err != memdb.ErrDbClosed {
t.Errorf("ExistsTxSha: hash %v exists when it shouldn't", t.Errorf("ExistsTxSha: unexpected error %v", err)
&coinbaseHash)
} }
if _, err := db.FetchTxBySha(genesisHash); err != memdb.ErrDbClosed { if _, err := db.FetchTxBySha(genesisHash); err != memdb.ErrDbClosed {
@ -103,9 +104,15 @@ func TestClosed(t *testing.T) {
t.Errorf("NewestSha: unexpected error %v", err) t.Errorf("NewestSha: unexpected error %v", err)
} }
// The following calls don't return errors from the interface to be able if err := db.Sync(); err != memdb.ErrDbClosed {
// to detect a closed database, so just call them to ensure there are no t.Errorf("Sync: unexpected error %v", err)
// panics. }
db.Sync()
db.RollbackClose() if err := db.RollbackClose(); err != memdb.ErrDbClosed {
t.Errorf("RollbackClose: unexpected error %v", err)
}
if err := db.Close(); err != memdb.ErrDbClosed {
t.Errorf("Close: unexpected error %v", err)
}
} }