add Walk function similar to filepath.Walk, add test for Walk

This commit is contained in:
Martin Bertschler 2015-11-05 14:00:51 +01:00
parent 171091926d
commit 62c9217de3
2 changed files with 118 additions and 8 deletions

82
fs.go
View File

@ -26,6 +26,7 @@ import (
"errors"
"io"
"os"
"path/filepath"
"sort"
"time"
)
@ -119,6 +120,87 @@ func ReadDir(dirname string, fs Fs) ([]os.FileInfo, error) {
return list, nil
}
// readDirNames reads the directory named by dirname and returns
// a sorted list of directory entries.
// adapted from https://golang.org/src/path/filepath/path.go
func readDirNames(dirname string, fs Fs) ([]string, error) {
f, err := fs.Open(dirname)
if err != nil {
return nil, err
}
names, err := f.Readdirnames(-1)
f.Close()
if err != nil {
return nil, err
}
sort.Strings(names)
return names, nil
}
// walk recursively descends path, calling walkFn
// adapted from https://golang.org/src/path/filepath/path.go
func walk(path string, info os.FileInfo, walkFn filepath.WalkFunc, fs Fs) error {
err := walkFn(path, info, nil)
if err != nil {
if info.IsDir() && err == filepath.SkipDir {
return nil
}
return err
}
if !info.IsDir() {
return nil
}
names, err := readDirNames(path, fs)
if err != nil {
return walkFn(path, info, err)
}
for _, name := range names {
filename := filepath.Join(path, name)
fileInfo, err := lstatIfOs(filename, fs)
if err != nil {
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
return err
}
} else {
err = walk(filename, fileInfo, walkFn, fs)
if err != nil {
if !fileInfo.IsDir() || err != filepath.SkipDir {
return err
}
}
}
}
return nil
}
// if the filesystem is OsFs use Lstat, else use fs.Stat
func lstatIfOs(path string, fs Fs) (info os.FileInfo, err error) {
_, ok := fs.(*OsFs)
if ok {
info, err = os.Lstat(path)
} else {
info, err = fs.Stat(path)
}
return
}
// Walk walks the file tree rooted at root, calling walkFn for each file or
// directory in the tree, including root. All errors that arise visiting files
// and directories are filtered by walkFn. The files are walked in lexical
// order, which makes the output deterministic but means that for very
// large directories Walk can be inefficient.
// Walk does not follow symbolic links.
func Walk(root string, walkFn filepath.WalkFunc, fs Fs) error {
info, err := lstatIfOs(root, fs)
if err != nil {
return walkFn(root, nil, err)
}
return walk(root, info, walkFn, fs)
}
// byName implements sort.Interface.
type byName []os.FileInfo

View File

@ -28,14 +28,6 @@ import (
"testing"
)
var dot = []string{
"fs.go",
"fs_test.go",
"httpFs.go",
"memfile.go",
"memmap.go",
}
var testDir = "/tmp/afero"
var testSubDir = "/tmp/afero/we/have/to/go/deeper"
var testName = "test.txt"
@ -406,6 +398,42 @@ func (m myFileInfo) String() string {
return out
}
func TestWalk(t *testing.T) {
outputs := make([]string, len(Fss))
for i, fs := range Fss {
walkFn := func(path string, info os.FileInfo, err error) error {
var size int64
if !info.IsDir() {
size = info.Size()
}
outputs[i] += fmt.Sprintln(path, info.Name(), size, info.IsDir(), err)
return nil
}
err := Walk(testDir, walkFn, fs)
if err != nil {
t.Error(err)
}
}
fail := false
for i, o := range outputs {
if i == 0 {
continue
}
if o != outputs[i-1] {
fail = true
break
}
}
if fail {
t.Log("Walk outputs not equal!")
for i, o := range outputs {
t.Log(Fss[i].Name())
t.Log(o)
}
t.Fail()
}
}
func TestReaddirAll(t *testing.T) {
defer removeTestDir(t)
for _, fs := range Fss {