From 28c1e86a27b2dc3ba59b8c4d69f15a8e54334a89 Mon Sep 17 00:00:00 2001 From: Ben Johnson Date: Sun, 12 Jan 2014 15:30:09 -0700 Subject: [PATCH] Mock OS and File. --- db.go | 16 ++++++++-------- db_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ file.go | 8 ++++++++ file_test.go | 29 +++++++++++++++++++++++++++++ os.go | 2 +- 5 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 file.go create mode 100644 file_test.go diff --git a/db.go b/db.go index 8176dd6..d9a6274 100644 --- a/db.go +++ b/db.go @@ -1,7 +1,7 @@ package bolt import ( - . "os" + "os" "sync" "syscall" "unsafe" @@ -26,9 +26,9 @@ type DB struct { sync.Mutex opened bool - os OS - file *File - metafile *File + os _os + file file + metafile file data []byte buf []byte meta0 *meta @@ -56,14 +56,14 @@ type DB struct { } func NewDB() *DB { - return &DB{os: &sysos{}} + return &DB{} } func (db *DB) Path() string { 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 db.Lock() 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. 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() 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() return err } diff --git a/db_test.go b/db_test.go index 534bc3c..9c6ad85 100644 --- a/db_test.go +++ b/db_test.go @@ -8,13 +8,46 @@ import ( "github.com/stretchr/testify/assert" ) +// Ensure that a database can be opened without error. func TestDBOpen(t *testing.T) { withDB(func(db *DB, path string) { err := db.Open(path, 0666) 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)) { f, _ := ioutil.TempFile("", "bolt-") path := f.Name() @@ -25,3 +58,11 @@ func withDB(fn func(*DB, string)) { db := NewDB() 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") +} diff --git a/file.go b/file.go new file mode 100644 index 0000000..4beb722 --- /dev/null +++ b/file.go @@ -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) +} diff --git a/file_test.go b/file_test.go new file mode 100644 index 0000000..3f33d14 --- /dev/null +++ b/file_test.go @@ -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) +} diff --git a/os.go b/os.go index f1de723..ab415a9 100644 --- a/os.go +++ b/os.go @@ -4,7 +4,7 @@ import ( "os" ) -type OS interface { +type _os interface { OpenFile(name string, flag int, perm os.FileMode) (file *os.File, err error) Stat(name string) (fi os.FileInfo, err error) Getpagesize() int