|
@@ -27,7 +27,7 @@ import (
|
|
// see readShortAt()
|
|
// see readShortAt()
|
|
const randomBlockShift = 14 // 128k
|
|
const randomBlockShift = 14 // 128k
|
|
|
|
|
|
-// fakefs is a fake filesystem for testing and benchmarking. It has the
|
|
|
|
|
|
+// fakeFS is a fake filesystem for testing and benchmarking. It has the
|
|
// following properties:
|
|
// following properties:
|
|
//
|
|
//
|
|
// - File metadata is kept in RAM. Specifically, we remember which files and
|
|
// - File metadata is kept in RAM. Specifically, we remember which files and
|
|
@@ -37,7 +37,7 @@ const randomBlockShift = 14 // 128k
|
|
// - File contents are generated pseudorandomly with just the file name as
|
|
// - File contents are generated pseudorandomly with just the file name as
|
|
// seed. Writes are discarded, other than having the effect of increasing
|
|
// seed. Writes are discarded, other than having the effect of increasing
|
|
// the file size. If you only write data that you've read from a file with
|
|
// the file size. If you only write data that you've read from a file with
|
|
-// the same name on a different fakefs, you'll never know the difference...
|
|
|
|
|
|
+// the same name on a different fakeFS, you'll never know the difference...
|
|
//
|
|
//
|
|
// - We totally ignore permissions - pretend you are root.
|
|
// - We totally ignore permissions - pretend you are root.
|
|
//
|
|
//
|
|
@@ -51,10 +51,10 @@ const randomBlockShift = 14 // 128k
|
|
// insens=b "true" makes filesystem case-insensitive Windows- or OSX-style (default false)
|
|
// insens=b "true" makes filesystem case-insensitive Windows- or OSX-style (default false)
|
|
// latency=d to set the amount of time each "disk" operation takes, where d is time.ParseDuration format
|
|
// latency=d to set the amount of time each "disk" operation takes, where d is time.ParseDuration format
|
|
//
|
|
//
|
|
-// - Two fakefs:s pointing at the same root path see the same files.
|
|
|
|
|
|
+// - Two fakeFS:s pointing at the same root path see the same files.
|
|
//
|
|
//
|
|
-type fakefs struct {
|
|
|
|
- counters fakefsCounters
|
|
|
|
|
|
+type fakeFS struct {
|
|
|
|
+ counters fakeFSCounters
|
|
uri string
|
|
uri string
|
|
mut sync.Mutex
|
|
mut sync.Mutex
|
|
root *fakeEntry
|
|
root *fakeEntry
|
|
@@ -63,7 +63,7 @@ type fakefs struct {
|
|
latency time.Duration
|
|
latency time.Duration
|
|
}
|
|
}
|
|
|
|
|
|
-type fakefsCounters struct {
|
|
|
|
|
|
+type fakeFSCounters struct {
|
|
Chmod int64
|
|
Chmod int64
|
|
Lchown int64
|
|
Lchown int64
|
|
Chtimes int64
|
|
Chtimes int64
|
|
@@ -81,13 +81,13 @@ type fakefsCounters struct {
|
|
}
|
|
}
|
|
|
|
|
|
var (
|
|
var (
|
|
- fakefsMut sync.Mutex
|
|
|
|
- fakefsFs = make(map[string]*fakefs)
|
|
|
|
|
|
+ fakeFSMut sync.Mutex
|
|
|
|
+ fakeFSCache = make(map[string]*fakeFS)
|
|
)
|
|
)
|
|
|
|
|
|
-func newFakeFilesystem(rootURI string, _ ...Option) *fakefs {
|
|
|
|
- fakefsMut.Lock()
|
|
|
|
- defer fakefsMut.Unlock()
|
|
|
|
|
|
+func newFakeFilesystem(rootURI string, _ ...Option) *fakeFS {
|
|
|
|
+ fakeFSMut.Lock()
|
|
|
|
+ defer fakeFSMut.Unlock()
|
|
|
|
|
|
root := rootURI
|
|
root := rootURI
|
|
var params url.Values
|
|
var params url.Values
|
|
@@ -97,12 +97,12 @@ func newFakeFilesystem(rootURI string, _ ...Option) *fakefs {
|
|
params = uri.Query()
|
|
params = uri.Query()
|
|
}
|
|
}
|
|
|
|
|
|
- if fs, ok := fakefsFs[rootURI]; ok {
|
|
|
|
|
|
+ if fs, ok := fakeFSCache[rootURI]; ok {
|
|
// Already have an fs at this path
|
|
// Already have an fs at this path
|
|
return fs
|
|
return fs
|
|
}
|
|
}
|
|
|
|
|
|
- fs := &fakefs{
|
|
|
|
|
|
+ fs := &fakeFS{
|
|
uri: "fake://" + rootURI,
|
|
uri: "fake://" + rootURI,
|
|
root: &fakeEntry{
|
|
root: &fakeEntry{
|
|
name: "/",
|
|
name: "/",
|
|
@@ -157,7 +157,7 @@ func newFakeFilesystem(rootURI string, _ ...Option) *fakefs {
|
|
// the filesystem initially.
|
|
// the filesystem initially.
|
|
fs.latency, _ = time.ParseDuration(params.Get("latency"))
|
|
fs.latency, _ = time.ParseDuration(params.Get("latency"))
|
|
|
|
|
|
- fakefsFs[root] = fs
|
|
|
|
|
|
+ fakeFSCache[root] = fs
|
|
return fs
|
|
return fs
|
|
}
|
|
}
|
|
|
|
|
|
@@ -183,7 +183,7 @@ type fakeEntry struct {
|
|
content []byte
|
|
content []byte
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) entryForName(name string) *fakeEntry {
|
|
|
|
|
|
+func (fs *fakeFS) entryForName(name string) *fakeEntry {
|
|
// bug: lookup doesn't work through symlinks.
|
|
// bug: lookup doesn't work through symlinks.
|
|
if fs.insens {
|
|
if fs.insens {
|
|
name = UnicodeLowercase(name)
|
|
name = UnicodeLowercase(name)
|
|
@@ -210,7 +210,7 @@ func (fs *fakefs) entryForName(name string) *fakeEntry {
|
|
return entry
|
|
return entry
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Chmod(name string, mode FileMode) error {
|
|
|
|
|
|
+func (fs *fakeFS) Chmod(name string, mode FileMode) error {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
defer fs.mut.Unlock()
|
|
defer fs.mut.Unlock()
|
|
fs.counters.Chmod++
|
|
fs.counters.Chmod++
|
|
@@ -223,7 +223,7 @@ func (fs *fakefs) Chmod(name string, mode FileMode) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Lchown(name string, uid, gid int) error {
|
|
|
|
|
|
+func (fs *fakeFS) Lchown(name string, uid, gid int) error {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
defer fs.mut.Unlock()
|
|
defer fs.mut.Unlock()
|
|
fs.counters.Lchown++
|
|
fs.counters.Lchown++
|
|
@@ -237,7 +237,7 @@ func (fs *fakefs) Lchown(name string, uid, gid int) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
|
|
|
|
|
+func (fs *fakeFS) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
defer fs.mut.Unlock()
|
|
defer fs.mut.Unlock()
|
|
fs.counters.Chtimes++
|
|
fs.counters.Chtimes++
|
|
@@ -250,7 +250,7 @@ func (fs *fakefs) Chtimes(name string, atime time.Time, mtime time.Time) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) create(name string) (*fakeEntry, error) {
|
|
|
|
|
|
+func (fs *fakeFS) create(name string) (*fakeEntry, error) {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
defer fs.mut.Unlock()
|
|
defer fs.mut.Unlock()
|
|
fs.counters.Create++
|
|
fs.counters.Create++
|
|
@@ -296,7 +296,7 @@ func (fs *fakefs) create(name string) (*fakeEntry, error) {
|
|
return new, nil
|
|
return new, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Create(name string) (File, error) {
|
|
|
|
|
|
+func (fs *fakeFS) Create(name string) (File, error) {
|
|
entry, err := fs.create(name)
|
|
entry, err := fs.create(name)
|
|
if err != nil {
|
|
if err != nil {
|
|
return nil, err
|
|
return nil, err
|
|
@@ -307,7 +307,7 @@ func (fs *fakefs) Create(name string) (File, error) {
|
|
return &fakeFile{fakeEntry: entry}, nil
|
|
return &fakeFile{fakeEntry: entry}, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) CreateSymlink(target, name string) error {
|
|
|
|
|
|
+func (fs *fakeFS) CreateSymlink(target, name string) error {
|
|
entry, err := fs.create(name)
|
|
entry, err := fs.create(name)
|
|
if err != nil {
|
|
if err != nil {
|
|
return err
|
|
return err
|
|
@@ -317,7 +317,7 @@ func (fs *fakefs) CreateSymlink(target, name string) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) DirNames(name string) ([]string, error) {
|
|
|
|
|
|
+func (fs *fakeFS) DirNames(name string) ([]string, error) {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
defer fs.mut.Unlock()
|
|
defer fs.mut.Unlock()
|
|
fs.counters.DirNames++
|
|
fs.counters.DirNames++
|
|
@@ -336,7 +336,7 @@ func (fs *fakefs) DirNames(name string) ([]string, error) {
|
|
return names, nil
|
|
return names, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Lstat(name string) (FileInfo, error) {
|
|
|
|
|
|
+func (fs *fakeFS) Lstat(name string) (FileInfo, error) {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
defer fs.mut.Unlock()
|
|
defer fs.mut.Unlock()
|
|
fs.counters.Lstat++
|
|
fs.counters.Lstat++
|
|
@@ -355,7 +355,7 @@ func (fs *fakefs) Lstat(name string) (FileInfo, error) {
|
|
return info, nil
|
|
return info, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Mkdir(name string, perm FileMode) error {
|
|
|
|
|
|
+func (fs *fakeFS) Mkdir(name string, perm FileMode) error {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
defer fs.mut.Unlock()
|
|
defer fs.mut.Unlock()
|
|
fs.counters.Mkdir++
|
|
fs.counters.Mkdir++
|
|
@@ -389,7 +389,7 @@ func (fs *fakefs) Mkdir(name string, perm FileMode) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) MkdirAll(name string, perm FileMode) error {
|
|
|
|
|
|
+func (fs *fakeFS) MkdirAll(name string, perm FileMode) error {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
defer fs.mut.Unlock()
|
|
defer fs.mut.Unlock()
|
|
fs.counters.MkdirAll++
|
|
fs.counters.MkdirAll++
|
|
@@ -426,7 +426,7 @@ func (fs *fakefs) MkdirAll(name string, perm FileMode) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Open(name string) (File, error) {
|
|
|
|
|
|
+func (fs *fakeFS) Open(name string) (File, error) {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
defer fs.mut.Unlock()
|
|
defer fs.mut.Unlock()
|
|
fs.counters.Open++
|
|
fs.counters.Open++
|
|
@@ -443,7 +443,7 @@ func (fs *fakefs) Open(name string) (File, error) {
|
|
return &fakeFile{fakeEntry: entry}, nil
|
|
return &fakeFile{fakeEntry: entry}, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) OpenFile(name string, flags int, mode FileMode) (File, error) {
|
|
|
|
|
|
+func (fs *fakeFS) OpenFile(name string, flags int, mode FileMode) (File, error) {
|
|
if flags&os.O_CREATE == 0 {
|
|
if flags&os.O_CREATE == 0 {
|
|
return fs.Open(name)
|
|
return fs.Open(name)
|
|
}
|
|
}
|
|
@@ -486,7 +486,7 @@ func (fs *fakefs) OpenFile(name string, flags int, mode FileMode) (File, error)
|
|
return &fakeFile{fakeEntry: newEntry}, nil
|
|
return &fakeFile{fakeEntry: newEntry}, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) ReadSymlink(name string) (string, error) {
|
|
|
|
|
|
+func (fs *fakeFS) ReadSymlink(name string) (string, error) {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
defer fs.mut.Unlock()
|
|
defer fs.mut.Unlock()
|
|
fs.counters.ReadSymlink++
|
|
fs.counters.ReadSymlink++
|
|
@@ -501,7 +501,7 @@ func (fs *fakefs) ReadSymlink(name string) (string, error) {
|
|
return entry.dest, nil
|
|
return entry.dest, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Remove(name string) error {
|
|
|
|
|
|
+func (fs *fakeFS) Remove(name string) error {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
defer fs.mut.Unlock()
|
|
defer fs.mut.Unlock()
|
|
fs.counters.Remove++
|
|
fs.counters.Remove++
|
|
@@ -524,7 +524,7 @@ func (fs *fakefs) Remove(name string) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) RemoveAll(name string) error {
|
|
|
|
|
|
+func (fs *fakeFS) RemoveAll(name string) error {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
defer fs.mut.Unlock()
|
|
defer fs.mut.Unlock()
|
|
fs.counters.RemoveAll++
|
|
fs.counters.RemoveAll++
|
|
@@ -536,7 +536,7 @@ func (fs *fakefs) RemoveAll(name string) error {
|
|
|
|
|
|
entry := fs.entryForName(filepath.Dir(name))
|
|
entry := fs.entryForName(filepath.Dir(name))
|
|
if entry == nil {
|
|
if entry == nil {
|
|
- return nil // all tested real systems exibit this behaviour
|
|
|
|
|
|
+ return nil // all tested real systems exhibit this behaviour
|
|
}
|
|
}
|
|
|
|
|
|
// RemoveAll is easy when the file system uses garbage collection under
|
|
// RemoveAll is easy when the file system uses garbage collection under
|
|
@@ -545,7 +545,7 @@ func (fs *fakefs) RemoveAll(name string) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Rename(oldname, newname string) error {
|
|
|
|
|
|
+func (fs *fakeFS) Rename(oldname, newname string) error {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
defer fs.mut.Unlock()
|
|
defer fs.mut.Unlock()
|
|
fs.counters.Rename++
|
|
fs.counters.Rename++
|
|
@@ -595,56 +595,56 @@ func (fs *fakefs) Rename(oldname, newname string) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Stat(name string) (FileInfo, error) {
|
|
|
|
|
|
+func (fs *fakeFS) Stat(name string) (FileInfo, error) {
|
|
return fs.Lstat(name)
|
|
return fs.Lstat(name)
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) SymlinksSupported() bool {
|
|
|
|
|
|
+func (fs *fakeFS) SymlinksSupported() bool {
|
|
return false
|
|
return false
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Walk(name string, walkFn WalkFunc) error {
|
|
|
|
|
|
+func (fs *fakeFS) Walk(name string, walkFn WalkFunc) error {
|
|
return errors.New("not implemented")
|
|
return errors.New("not implemented")
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, <-chan error, error) {
|
|
|
|
|
|
+func (fs *fakeFS) Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, <-chan error, error) {
|
|
return nil, nil, ErrWatchNotSupported
|
|
return nil, nil, ErrWatchNotSupported
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Hide(name string) error {
|
|
|
|
|
|
+func (fs *fakeFS) Hide(name string) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Unhide(name string) error {
|
|
|
|
|
|
+func (fs *fakeFS) Unhide(name string) error {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Glob(pattern string) ([]string, error) {
|
|
|
|
|
|
+func (fs *fakeFS) Glob(pattern string) ([]string, error) {
|
|
// gnnh we don't seem to actually require this in practice
|
|
// gnnh we don't seem to actually require this in practice
|
|
return nil, errors.New("not implemented")
|
|
return nil, errors.New("not implemented")
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Roots() ([]string, error) {
|
|
|
|
|
|
+func (fs *fakeFS) Roots() ([]string, error) {
|
|
return []string{"/"}, nil
|
|
return []string{"/"}, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Usage(name string) (Usage, error) {
|
|
|
|
|
|
+func (fs *fakeFS) Usage(name string) (Usage, error) {
|
|
return Usage{}, errors.New("not implemented")
|
|
return Usage{}, errors.New("not implemented")
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Type() FilesystemType {
|
|
|
|
|
|
+func (fs *fakeFS) Type() FilesystemType {
|
|
return FilesystemTypeFake
|
|
return FilesystemTypeFake
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) URI() string {
|
|
|
|
|
|
+func (fs *fakeFS) URI() string {
|
|
return fs.uri
|
|
return fs.uri
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) Options() []Option {
|
|
|
|
|
|
+func (fs *fakeFS) Options() []Option {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) SameFile(fi1, fi2 FileInfo) bool {
|
|
|
|
|
|
+func (fs *fakeFS) SameFile(fi1, fi2 FileInfo) bool {
|
|
// BUG: real systems base file sameness on path, inodes, etc
|
|
// BUG: real systems base file sameness on path, inodes, etc
|
|
// we try our best, but FileInfo just doesn't have enough data
|
|
// we try our best, but FileInfo just doesn't have enough data
|
|
// so there be false positives, especially on Windows
|
|
// so there be false positives, especially on Windows
|
|
@@ -659,17 +659,25 @@ func (fs *fakefs) SameFile(fi1, fi2 FileInfo) bool {
|
|
return ok && fi1.ModTime().Equal(fi2.ModTime()) && fi1.Mode() == fi2.Mode() && fi1.IsDir() == fi2.IsDir() && fi1.IsRegular() == fi2.IsRegular() && fi1.IsSymlink() == fi2.IsSymlink() && fi1.Owner() == fi2.Owner() && fi1.Group() == fi2.Group()
|
|
return ok && fi1.ModTime().Equal(fi2.ModTime()) && fi1.Mode() == fi2.Mode() && fi1.IsDir() == fi2.IsDir() && fi1.IsRegular() == fi2.IsRegular() && fi1.IsSymlink() == fi2.IsSymlink() && fi1.Owner() == fi2.Owner() && fi1.Group() == fi2.Group()
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) resetCounters() {
|
|
|
|
|
|
+func (fs *fakeFS) underlying() (Filesystem, bool) {
|
|
|
|
+ return nil, false
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (fs *fakeFS) wrapperType() filesystemWrapperType {
|
|
|
|
+ return filesystemWrapperTypeNone
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (fs *fakeFS) resetCounters() {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
- fs.counters = fakefsCounters{}
|
|
|
|
|
|
+ fs.counters = fakeFSCounters{}
|
|
fs.mut.Unlock()
|
|
fs.mut.Unlock()
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) reportMetricsPerOp(b *testing.B) {
|
|
|
|
|
|
+func (fs *fakeFS) reportMetricsPerOp(b *testing.B) {
|
|
fs.reportMetricsPer(b, 1, "op")
|
|
fs.reportMetricsPer(b, 1, "op")
|
|
}
|
|
}
|
|
|
|
|
|
-func (fs *fakefs) reportMetricsPer(b *testing.B, divisor float64, unit string) {
|
|
|
|
|
|
+func (fs *fakeFS) reportMetricsPer(b *testing.B, divisor float64, unit string) {
|
|
fs.mut.Lock()
|
|
fs.mut.Lock()
|
|
defer fs.mut.Unlock()
|
|
defer fs.mut.Unlock()
|
|
b.ReportMetric(float64(fs.counters.Lstat)/divisor/float64(b.N), "Lstat/"+unit)
|
|
b.ReportMetric(float64(fs.counters.Lstat)/divisor/float64(b.N), "Lstat/"+unit)
|