Make bolt.Open return the documented errors

- ErrInvalid is returned when a data file is not a Bolt-formatted
  database.
- ErrVersionMismatch is returned when the data file was created with a
  different version of Bolt.
- ErrChecksum is returned when either meta page checksum does not match.

Also:
- Do not wrap errors from os.Stat, so that a caller could handle os.Stat
  errors just like it can handle errors from os.Open that bolt.Open
  might return.
- Name tests consistently, following the pattern "TestOpen_*".
- Remove deferred calls to `os.Remove(path)`.
  The calls are not only unnecessary, but also in all cases `os.Remove`
  returns an error that is ignored. All those calls are meant to remove
  a file that was already removed by `tmpfile()`.
- Combine "bad path" tests and use filepath.Join to build the path.
master
Rodolfo Carvalho 2015-12-10 18:39:03 +01:00
parent b34b35ea8d
commit 058a7ab347
2 changed files with 98 additions and 28 deletions

8
db.go
View File

@ -177,7 +177,7 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
// Initialize the database if it doesn't exist. // Initialize the database if it doesn't exist.
if info, err := db.file.Stat(); err != nil { if info, err := db.file.Stat(); err != nil {
return nil, fmt.Errorf("stat error: %s", err) return nil, err
} else if info.Size() == 0 { } else if info.Size() == 0 {
// Initialize new files with meta pages. // Initialize new files with meta pages.
if err := db.init(); err != nil { if err := db.init(); err != nil {
@ -189,7 +189,7 @@ func Open(path string, mode os.FileMode, options *Options) (*DB, error) {
if _, err := db.file.ReadAt(buf[:], 0); err == nil { if _, err := db.file.ReadAt(buf[:], 0); err == nil {
m := db.pageInBuffer(buf[:], 0).meta() m := db.pageInBuffer(buf[:], 0).meta()
if err := m.validate(); err != nil { if err := m.validate(); err != nil {
return nil, fmt.Errorf("meta0 error: %s", err) return nil, err
} }
db.pageSize = int(m.pageSize) db.pageSize = int(m.pageSize)
} }
@ -253,10 +253,10 @@ func (db *DB) mmap(minsz int) error {
// Validate the meta pages. // Validate the meta pages.
if err := db.meta0.validate(); err != nil { if err := db.meta0.validate(); err != nil {
return fmt.Errorf("meta0 error: %s", err) return err
} }
if err := db.meta1.validate(); err != nil { if err := db.meta1.validate(); err != nil {
return fmt.Errorf("meta1 error: %s", err) return err
} }
return nil return nil

View File

@ -7,29 +7,48 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath"
"regexp" "regexp"
"runtime" "runtime"
"sort" "sort"
"strings" "strings"
"testing" "testing"
"time" "time"
"unsafe"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
) )
var statsFlag = flag.Bool("stats", false, "show performance stats") var statsFlag = flag.Bool("stats", false, "show performance stats")
// Ensure that opening a database with a bad path returns an error. // version is the data file format version.
func TestOpen_BadPath(t *testing.T) { const version = 2
db, err := bolt.Open("", 0666, nil)
assert(t, err != nil, "err: %s", err) // magic is the marker value to indicate that a file is a Bolt DB.
assert(t, db == nil, "") const magic uint32 = 0xED0CDAED
// pageSize is the size of one page in the data file.
const pageSize = 4096
// pageHeaderSize is the size of a page header.
const pageHeaderSize = 16
// meta represents a simplified version of a database meta page for testing.
type meta struct {
magic uint32
version uint32
_ uint32
_ uint32
_ [16]byte
_ uint64
_ uint64
_ uint64
checksum uint64
} }
// Ensure that a database can be opened without error. // Ensure that a database can be opened without error.
func TestOpen(t *testing.T) { func TestOpen(t *testing.T) {
path := tempfile() path := tempfile()
defer os.Remove(path)
db, err := bolt.Open(path, 0666, nil) db, err := bolt.Open(path, 0666, nil)
assert(t, db != nil, "") assert(t, db != nil, "")
ok(t, err) ok(t, err)
@ -37,6 +56,73 @@ func TestOpen(t *testing.T) {
ok(t, db.Close()) ok(t, db.Close())
} }
// Ensure that opening a database with a bad path returns an error.
func TestOpen_BadPath(t *testing.T) {
for _, path := range []string{
"",
filepath.Join(tempfile(), "youre-not-my-real-parent"),
} {
t.Logf("path = %q", path)
db, err := bolt.Open(path, 0666, nil)
assert(t, err != nil, "err: %s", err)
equals(t, path, err.(*os.PathError).Path)
equals(t, "open", err.(*os.PathError).Op)
equals(t, (*bolt.DB)(nil), db)
}
}
// Ensure that opening a file with wrong checksum returns ErrChecksum.
func TestOpen_ErrChecksum(t *testing.T) {
buf := make([]byte, pageSize)
meta := (*meta)(unsafe.Pointer(&buf[0]))
meta.magic = magic
meta.version = version
meta.checksum = 123
path := tempfile()
f, err := os.Create(path)
equals(t, nil, err)
f.WriteAt(buf, pageHeaderSize)
f.Close()
defer os.Remove(path)
_, err = bolt.Open(path, 0666, nil)
equals(t, bolt.ErrChecksum, err)
}
// Ensure that opening a file that is not a Bolt database returns ErrInvalid.
func TestOpen_ErrInvalid(t *testing.T) {
path := tempfile()
f, err := os.Create(path)
equals(t, nil, err)
fmt.Fprintln(f, "this is not a bolt database")
f.Close()
defer os.Remove(path)
_, err = bolt.Open(path, 0666, nil)
equals(t, bolt.ErrInvalid, err)
}
// Ensure that opening a file created with a different version of Bolt returns
// ErrVersionMismatch.
func TestOpen_ErrVersionMismatch(t *testing.T) {
buf := make([]byte, pageSize)
meta := (*meta)(unsafe.Pointer(&buf[0]))
meta.magic = magic
meta.version = version + 100
path := tempfile()
f, err := os.Create(path)
equals(t, nil, err)
f.WriteAt(buf, pageHeaderSize)
f.Close()
defer os.Remove(path)
_, err = bolt.Open(path, 0666, nil)
equals(t, bolt.ErrVersionMismatch, err)
}
// Ensure that opening an already open database file will timeout. // Ensure that opening an already open database file will timeout.
func TestOpen_Timeout(t *testing.T) { func TestOpen_Timeout(t *testing.T) {
if runtime.GOOS == "solaris" { if runtime.GOOS == "solaris" {
@ -44,7 +130,6 @@ func TestOpen_Timeout(t *testing.T) {
} }
path := tempfile() path := tempfile()
defer os.Remove(path)
// Open a data file. // Open a data file.
db0, err := bolt.Open(path, 0666, nil) db0, err := bolt.Open(path, 0666, nil)
@ -68,7 +153,6 @@ func TestOpen_Wait(t *testing.T) {
} }
path := tempfile() path := tempfile()
defer os.Remove(path)
// Open a data file. // Open a data file.
db0, err := bolt.Open(path, 0666, nil) db0, err := bolt.Open(path, 0666, nil)
@ -179,7 +263,6 @@ func TestOpen_Size_Large(t *testing.T) {
// Ensure that a re-opened database is consistent. // Ensure that a re-opened database is consistent.
func TestOpen_Check(t *testing.T) { func TestOpen_Check(t *testing.T) {
path := tempfile() path := tempfile()
defer os.Remove(path)
db, err := bolt.Open(path, 0666, nil) db, err := bolt.Open(path, 0666, nil)
ok(t, err) ok(t, err)
@ -192,26 +275,14 @@ func TestOpen_Check(t *testing.T) {
db.Close() db.Close()
} }
// Ensure that the database returns an error if the file handle cannot be open.
func TestDB_Open_FileError(t *testing.T) {
path := tempfile()
defer os.Remove(path)
_, err := bolt.Open(path+"/youre-not-my-real-parent", 0666, nil)
assert(t, err.(*os.PathError) != nil, "")
equals(t, path+"/youre-not-my-real-parent", err.(*os.PathError).Path)
equals(t, "open", err.(*os.PathError).Op)
}
// Ensure that write errors to the meta file handler during initialization are returned. // Ensure that write errors to the meta file handler during initialization are returned.
func TestDB_Open_MetaInitWriteError(t *testing.T) { func TestOpen_MetaInitWriteError(t *testing.T) {
t.Skip("pending") t.Skip("pending")
} }
// Ensure that a database that is too small returns an error. // Ensure that a database that is too small returns an error.
func TestDB_Open_FileTooSmall(t *testing.T) { func TestOpen_FileTooSmall(t *testing.T) {
path := tempfile() path := tempfile()
defer os.Remove(path)
db, err := bolt.Open(path, 0666, nil) db, err := bolt.Open(path, 0666, nil)
ok(t, err) ok(t, err)
@ -235,7 +306,6 @@ func TestOpen_ReadOnly(t *testing.T) {
bucket, key, value := []byte(`bucket`), []byte(`key`), []byte(`value`) bucket, key, value := []byte(`bucket`), []byte(`key`), []byte(`value`)
path := tempfile() path := tempfile()
defer os.Remove(path)
// Open in read-write mode. // Open in read-write mode.
db, err := bolt.Open(path, 0666, nil) db, err := bolt.Open(path, 0666, nil)