diff --git a/fusetesting/stat.go b/fusetesting/stat.go new file mode 100644 index 0000000..c084303 --- /dev/null +++ b/fusetesting/stat.go @@ -0,0 +1,107 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fusetesting + +import ( + "fmt" + "os" + "reflect" + "time" + + "github.com/jacobsa/oglematchers" +) + +// Match os.FileInfo values that specify an mtime equal to the given time. On +// platforms where the Sys() method returns a struct containing an mtime, check +// also that it matches. +func MtimeIs(expected time.Time) oglematchers.Matcher { + return oglematchers.NewMatcher( + func(c interface{}) error { return mtimeIs(c, expected) }, + fmt.Sprintf("mtime is %v", expected)) +} + +func mtimeIs(c interface{}, expected time.Time) error { + fi, ok := c.(os.FileInfo) + if !ok { + return fmt.Errorf("which is of type %v", reflect.TypeOf(c)) + } + + // Check ModTime(). + if fi.ModTime() != expected { + d := fi.ModTime().Sub(expected) + return fmt.Errorf("which has mtime %v, off by %v", fi.ModTime(), d) + } + + // Check Sys(). + if sysMtime, ok := extractMtime(fi.Sys()); ok { + if sysMtime != expected { + d := sysMtime.Sub(expected) + return fmt.Errorf("which has Sys() mtime %v, off by %v", sysMtime, d) + } + } + + return nil +} + +// Match os.FileInfo values that specify a file birth time equal to the given +// time. On platforms where there is no birth time available, match all +// os.FileInfo values. +func BirthtimeIs(expected time.Time) oglematchers.Matcher { + return oglematchers.NewMatcher( + func(c interface{}) error { return birthtimeIs(c, expected) }, + fmt.Sprintf("birthtime is %v", expected)) +} + +func birthtimeIs(c interface{}, expected time.Time) error { + fi, ok := c.(os.FileInfo) + if !ok { + return fmt.Errorf("which is of type %v", reflect.TypeOf(c)) + } + + // Check Sys(). + if sysBirthtime, ok := extractBirthtime(fi.Sys()); ok { + if sysBirthtime != expected { + d := sysBirthtime.Sub(expected) + return fmt.Errorf( + "which has Sys() birthtime %v, off by %v", + sysBirthtime, + d) + } + } + + return nil +} + +// Match os.FileInfo values that specify a number of links equal to the given +// number. On platforms where there is no nlink field available, match all +// os.FileInfo values. +func NlinkIs(expected uint64) oglematchers.Matcher { + return oglematchers.NewMatcher( + func(c interface{}) error { return nlinkIs(c, expected) }, + fmt.Sprintf("nlink is %v", expected)) +} + +func nlinkIs(c interface{}, expected uint64) error { + fi, ok := c.(os.FileInfo) + if !ok { + return fmt.Errorf("which is of type %v", reflect.TypeOf(c)) + } + + if actual, ok := extractNlink(fi.Sys()); ok && actual != expected { + return fmt.Errorf("which has nlink == %v", actual) + } + + return nil +} diff --git a/fusetesting/stat_darwin.go b/fusetesting/stat_darwin.go new file mode 100644 index 0000000..9b4ebf5 --- /dev/null +++ b/fusetesting/stat_darwin.go @@ -0,0 +1,38 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fusetesting + +import ( + "syscall" + "time" +) + +func extractMtime(sys interface{}) (mtime time.Time, ok bool) { + mtime = time.Unix(sys.(*syscall.Stat_t).Mtimespec.Unix()) + ok = true + return +} + +func extractBirthtime(sys interface{}) (birthtime time.Time, ok bool) { + birthtime = time.Unix(sys.(*syscall.Stat_t).Birthtimespec.Unix()) + ok = true + return +} + +func extractNlink(sys interface{}) (nlink uint64, ok bool) { + nlink = uint64(sys.(*syscall.Stat_t).Nlink) + ok = true + return +} diff --git a/fusetesting/stat_linux.go b/fusetesting/stat_linux.go new file mode 100644 index 0000000..eec3568 --- /dev/null +++ b/fusetesting/stat_linux.go @@ -0,0 +1,36 @@ +// Copyright 2015 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fusetesting + +import ( + "syscall" + "time" +) + +func extractMtime(sys interface{}) (mtime time.Time, ok bool) { + mtime = time.Unix(sys.(*syscall.Stat_t).Mtim.Unix()) + ok = true + return +} + +func extractBirthtime(sys interface{}) (birthtime time.Time, ok bool) { + return +} + +func extractNlink(sys interface{}) (nlink uint64, ok bool) { + nlink = sys.(*syscall.Stat_t).Nlink + ok = true + return +} diff --git a/samples/memfs/memfs_test.go b/samples/memfs/memfs_test.go index f73fcee..a1b89f6 100644 --- a/samples/memfs/memfs_test.go +++ b/samples/memfs/memfs_test.go @@ -28,6 +28,7 @@ import ( "time" "github.com/jacobsa/fuse" + "github.com/jacobsa/fuse/fusetesting" "github.com/jacobsa/fuse/samples/memfs" "github.com/jacobsa/gcsfuse/timeutil" . "github.com/jacobsa/oglematchers" @@ -69,10 +70,6 @@ func currentGid() uint32 { return uint32(gid) } -func timespecToTime(ts syscall.Timespec) time.Time { - return time.Unix(ts.Sec, ts.Nsec) -} - //////////////////////////////////////////////////////////////////////// // Boilerplate //////////////////////////////////////////////////////////////////////// @@ -187,7 +184,8 @@ func (t *MemFSTest) Mkdir_OneLevel() { ExpectEq("dir", fi.Name()) ExpectEq(0, fi.Size()) ExpectEq(os.ModeDir|0754, fi.Mode()) - ExpectEq(0, fi.ModTime().Sub(createTime)) + ExpectThat(fi, fusetesting.MtimeIs(createTime)) + ExpectThat(fi, fusetesting.BirthtimeIs(createTime)) ExpectTrue(fi.IsDir()) ExpectNe(0, stat.Ino) @@ -195,8 +193,6 @@ func (t *MemFSTest) Mkdir_OneLevel() { ExpectEq(currentUid(), stat.Uid) ExpectEq(currentGid(), stat.Gid) ExpectEq(0, stat.Size) - ExpectEq(0, timespecToTime(stat.Mtimespec).Sub(createTime)) - ExpectEq(0, timespecToTime(stat.Birthtimespec).Sub(createTime)) // Check the root's mtime. fi, err = os.Stat(t.mfs.Dir()) @@ -250,7 +246,8 @@ func (t *MemFSTest) Mkdir_TwoLevels() { ExpectEq("dir", fi.Name()) ExpectEq(0, fi.Size()) ExpectEq(os.ModeDir|0754, fi.Mode()) - ExpectEq(0, fi.ModTime().Sub(createTime)) + ExpectThat(fi, fusetesting.MtimeIs(createTime)) + ExpectThat(fi, fusetesting.BirthtimeIs(createTime)) ExpectTrue(fi.IsDir()) ExpectNe(0, stat.Ino) @@ -258,8 +255,6 @@ func (t *MemFSTest) Mkdir_TwoLevels() { ExpectEq(currentUid(), stat.Uid) ExpectEq(currentGid(), stat.Gid) ExpectEq(0, stat.Size) - ExpectEq(0, timespecToTime(stat.Mtimespec).Sub(createTime)) - ExpectEq(0, timespecToTime(stat.Birthtimespec).Sub(createTime)) // Check the parent's mtime. fi, err = os.Stat(path.Join(t.mfs.Dir(), "parent")) @@ -363,7 +358,8 @@ func (t *MemFSTest) CreateNewFile_InRoot() { ExpectEq("foo", fi.Name()) ExpectEq(len(contents), fi.Size()) ExpectEq(0400, fi.Mode()) - ExpectEq(0, fi.ModTime().Sub(createTime)) + ExpectThat(fi, fusetesting.MtimeIs(createTime)) + ExpectThat(fi, fusetesting.BirthtimeIs(createTime)) ExpectFalse(fi.IsDir()) ExpectNe(0, stat.Ino) @@ -371,8 +367,6 @@ func (t *MemFSTest) CreateNewFile_InRoot() { ExpectEq(currentUid(), stat.Uid) ExpectEq(currentGid(), stat.Gid) ExpectEq(len(contents), stat.Size) - ExpectEq(0, timespecToTime(stat.Mtimespec).Sub(createTime)) - ExpectEq(0, timespecToTime(stat.Birthtimespec).Sub(createTime)) // Read it back. slice, err := ioutil.ReadFile(fileName) @@ -409,7 +403,8 @@ func (t *MemFSTest) CreateNewFile_InSubDir() { ExpectEq("foo", fi.Name()) ExpectEq(len(contents), fi.Size()) ExpectEq(0400, fi.Mode()) - ExpectEq(0, fi.ModTime().Sub(createTime)) + ExpectThat(fi, fusetesting.MtimeIs(createTime)) + ExpectThat(fi, fusetesting.BirthtimeIs(createTime)) ExpectFalse(fi.IsDir()) ExpectNe(0, stat.Ino) @@ -417,8 +412,6 @@ func (t *MemFSTest) CreateNewFile_InSubDir() { ExpectEq(currentUid(), stat.Uid) ExpectEq(currentGid(), stat.Gid) ExpectEq(len(contents), stat.Size) - ExpectEq(0, timespecToTime(stat.Mtimespec).Sub(createTime)) - ExpectEq(0, timespecToTime(stat.Birthtimespec).Sub(createTime)) // Read it back. slice, err := ioutil.ReadFile(fileName) @@ -463,7 +456,8 @@ func (t *MemFSTest) ModifyExistingFile_InRoot() { ExpectEq("foo", fi.Name()) ExpectEq(len("Hello, world!"), fi.Size()) ExpectEq(0600, fi.Mode()) - ExpectEq(0, fi.ModTime().Sub(modifyTime)) + ExpectThat(fi, fusetesting.MtimeIs(modifyTime)) + ExpectThat(fi, fusetesting.BirthtimeIs(createTime)) ExpectFalse(fi.IsDir()) ExpectNe(0, stat.Ino) @@ -471,8 +465,6 @@ func (t *MemFSTest) ModifyExistingFile_InRoot() { ExpectEq(currentUid(), stat.Uid) ExpectEq(currentGid(), stat.Gid) ExpectEq(len("Hello, world!"), stat.Size) - ExpectEq(0, timespecToTime(stat.Mtimespec).Sub(modifyTime)) - ExpectEq(0, timespecToTime(stat.Birthtimespec).Sub(createTime)) // Read the file back. slice, err := ioutil.ReadFile(fileName) @@ -522,7 +514,8 @@ func (t *MemFSTest) ModifyExistingFile_InSubDir() { ExpectEq("foo", fi.Name()) ExpectEq(len("Hello, world!"), fi.Size()) ExpectEq(0600, fi.Mode()) - ExpectEq(0, fi.ModTime().Sub(modifyTime)) + ExpectThat(fi, fusetesting.MtimeIs(modifyTime)) + ExpectThat(fi, fusetesting.BirthtimeIs(createTime)) ExpectFalse(fi.IsDir()) ExpectNe(0, stat.Ino) @@ -530,8 +523,6 @@ func (t *MemFSTest) ModifyExistingFile_InSubDir() { ExpectEq(currentUid(), stat.Uid) ExpectEq(currentGid(), stat.Gid) ExpectEq(len("Hello, world!"), stat.Size) - ExpectEq(0, timespecToTime(stat.Mtimespec).Sub(modifyTime)) - ExpectEq(0, timespecToTime(stat.Birthtimespec).Sub(createTime)) // Read the file back. slice, err := ioutil.ReadFile(fileName)