Mock OS and File.

pull/34/head
Ben Johnson 2014-01-12 15:30:09 -07:00
parent 746c287b26
commit 28c1e86a27
5 changed files with 87 additions and 9 deletions

16
db.go
View File

@ -1,7 +1,7 @@
package bolt package bolt
import ( import (
. "os" "os"
"sync" "sync"
"syscall" "syscall"
"unsafe" "unsafe"
@ -26,9 +26,9 @@ type DB struct {
sync.Mutex sync.Mutex
opened bool opened bool
os OS os _os
file *File file file
metafile *File metafile file
data []byte data []byte
buf []byte buf []byte
meta0 *meta meta0 *meta
@ -56,14 +56,14 @@ type DB struct {
} }
func NewDB() *DB { func NewDB() *DB {
return &DB{os: &sysos{}} return &DB{}
} }
func (db *DB) Path() string { func (db *DB) Path() string {
return db.path return db.path
} }
func (db *DB) Open(path string, mode FileMode) error { func (db *DB) Open(path string, mode os.FileMode) error {
var err error var err error
db.Lock() db.Lock()
defer db.Unlock() defer db.Unlock()
@ -79,11 +79,11 @@ func (db *DB) Open(path string, mode FileMode) error {
// Open data file and separate sync handler for metadata writes. // Open data file and separate sync handler for metadata writes.
db.path = path db.path = path
if db.file, err = db.os.OpenFile(db.path, O_RDWR|O_CREATE, mode); err != nil { if db.file, err = db.os.OpenFile(db.path, os.O_RDWR|os.O_CREATE, mode); err != nil {
db.close() db.close()
return err return err
} }
if db.metafile, err = db.os.OpenFile(db.path, O_RDWR|O_SYNC, mode); err != nil { if db.metafile, err = db.os.OpenFile(db.path, os.O_RDWR|os.O_SYNC, mode); err != nil {
db.close() db.close()
return err return err
} }

View File

@ -8,13 +8,46 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// Ensure that a database can be opened without error.
func TestDBOpen(t *testing.T) { func TestDBOpen(t *testing.T) {
withDB(func(db *DB, path string) { withDB(func(db *DB, path string) {
err := db.Open(path, 0666) err := db.Open(path, 0666)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, db.Path(), path)
}) })
} }
// Ensure that the database returns an error if already open.
func TestDBReopen(t *testing.T) {
withDB(func(db *DB, path string) {
db.Open(path, 0666)
err := db.Open(path, 0666)
assert.Equal(t, err, DatabaseAlreadyOpenedError)
})
}
// Ensure that the database returns an error if the file handle cannot be open.
func TestDBOpenFileError(t *testing.T) {
withMockDB(func(db *DB, mockos *mockos, path string) {
exp := &os.PathError{}
mockos.On("OpenFile", path, os.O_RDWR|os.O_CREATE, os.FileMode(0666)).Return((*os.File)(nil), exp)
err := db.Open(path, 0666)
assert.Equal(t, err, exp)
})
}
// Ensure that the database returns an error if the meta file handle cannot be open.
func TestDBOpenMetaFileError(t *testing.T) {
withMockDB(func(db *DB, mockos *mockos, path string) {
exp := &os.PathError{}
mockos.On("OpenFile", path, os.O_RDWR|os.O_CREATE, os.FileMode(0666)).Return(&os.File{}, nil)
mockos.On("OpenFile", path, os.O_RDWR|os.O_SYNC, os.FileMode(0666)).Return((*os.File)(nil), exp)
err := db.Open(path, 0666)
assert.Equal(t, err, exp)
})
}
// withDB executes a function with a database reference.
func withDB(fn func(*DB, string)) { func withDB(fn func(*DB, string)) {
f, _ := ioutil.TempFile("", "bolt-") f, _ := ioutil.TempFile("", "bolt-")
path := f.Name() path := f.Name()
@ -25,3 +58,11 @@ func withDB(fn func(*DB, string)) {
db := NewDB() db := NewDB()
fn(db, path) fn(db, path)
} }
// withMockDB executes a function with a database reference and a mock filesystem.
func withMockDB(fn func(*DB, *mockos, string)) {
os := &mockos{}
db := NewDB()
db.os = os
fn(db, os, "/mock/db")
}

8
file.go Normal file
View File

@ -0,0 +1,8 @@
package bolt
type file interface {
Fd() uintptr
Name() string
ReadAt(b []byte, off int64) (n int, err error)
WriteAt(b []byte, off int64) (n int, err error)
}

29
file_test.go Normal file
View File

@ -0,0 +1,29 @@
package bolt
import (
"github.com/stretchr/testify/mock"
)
type mockfile struct {
mock.Mock
fd uintptr
name string
}
func (m *mockfile) Fd() uintptr {
return m.fd
}
func (m *mockfile) Name() string {
return m.name
}
func (m *mockfile) ReadAt(b []byte, off int64) (n int, err error) {
args := m.Called(b, off)
return args.Int(0), args.Error(1)
}
func (m *mockfile) WriteAt(b []byte, off int64) (n int, err error) {
args := m.Called(b, off)
return args.Int(0), args.Error(1)
}

2
os.go
View File

@ -4,7 +4,7 @@ import (
"os" "os"
) )
type OS interface { type _os interface {
OpenFile(name string, flag int, perm os.FileMode) (file *os.File, err error) OpenFile(name string, flag int, perm os.FileMode) (file *os.File, err error)
Stat(name string) (fi os.FileInfo, err error) Stat(name string) (fi os.FileInfo, err error)
Getpagesize() int Getpagesize() int