Merge pull request #174 from colincross/glob
Fix recursive glob on MockFs
This commit is contained in:
commit
afa12b4744
3 changed files with 123 additions and 53 deletions
|
@ -22,6 +22,8 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Based on Andrew Gerrand's "10 things you (probably) dont' know about Go"
|
// Based on Andrew Gerrand's "10 things you (probably) dont' know about Go"
|
||||||
|
@ -53,6 +55,8 @@ func MockFs(files map[string][]byte) FileSystem {
|
||||||
fs.all = append(fs.all, d)
|
fs.all = append(fs.all, d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sort.Strings(fs.all)
|
||||||
|
|
||||||
return fs
|
return fs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +67,7 @@ type FileSystem interface {
|
||||||
glob(pattern string) (matches []string, err error)
|
glob(pattern string) (matches []string, err error)
|
||||||
IsDir(name string) (bool, error)
|
IsDir(name string) (bool, error)
|
||||||
Lstat(name string) (os.FileInfo, error)
|
Lstat(name string) (os.FileInfo, error)
|
||||||
|
ListDirsRecursive(name string) (dirs []string, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// osFs implements FileSystem using the local disk.
|
// osFs implements FileSystem using the local disk.
|
||||||
|
@ -100,6 +105,27 @@ func (osFs) Lstat(path string) (stats os.FileInfo, err error) {
|
||||||
return os.Lstat(path)
|
return os.Lstat(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns a list of all directories under dir
|
||||||
|
func (osFs) ListDirsRecursive(name string) (dirs []string, err error) {
|
||||||
|
err = filepath.Walk(name, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Mode().IsDir() {
|
||||||
|
name := info.Name()
|
||||||
|
if name[0] == '.' && name != "." {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
|
||||||
|
dirs = append(dirs, path)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return dirs, err
|
||||||
|
}
|
||||||
|
|
||||||
type mockFs struct {
|
type mockFs struct {
|
||||||
files map[string][]byte
|
files map[string][]byte
|
||||||
dirs map[string]bool
|
dirs map[string]bool
|
||||||
|
@ -150,6 +176,10 @@ func (m *mockFs) glob(pattern string) ([]string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if f == "." && f != pattern {
|
||||||
|
// filepath.Glob won't return "." unless the pattern was "."
|
||||||
|
match = false
|
||||||
|
}
|
||||||
if match {
|
if match {
|
||||||
matches = append(matches, f)
|
matches = append(matches, f)
|
||||||
}
|
}
|
||||||
|
@ -160,3 +190,23 @@ func (m *mockFs) glob(pattern string) ([]string, error) {
|
||||||
func (m *mockFs) Lstat(path string) (stats os.FileInfo, err error) {
|
func (m *mockFs) Lstat(path string) (stats os.FileInfo, err error) {
|
||||||
return nil, errors.New("Lstat is not yet implemented in MockFs")
|
return nil, errors.New("Lstat is not yet implemented in MockFs")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *mockFs) ListDirsRecursive(name string) (dirs []string, err error) {
|
||||||
|
name = filepath.Clean(name)
|
||||||
|
dirs = append(dirs, name)
|
||||||
|
if name == "." {
|
||||||
|
name = ""
|
||||||
|
} else if name != "/" {
|
||||||
|
name = name + "/"
|
||||||
|
}
|
||||||
|
for _, f := range m.all {
|
||||||
|
if _, isDir := m.dirs[f]; isDir && filepath.Base(f)[0] != '.' {
|
||||||
|
if strings.HasPrefix(f, name) &&
|
||||||
|
strings.HasPrefix(f, "/") == strings.HasPrefix(name, "/") {
|
||||||
|
dirs = append(dirs, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirs, nil
|
||||||
|
}
|
||||||
|
|
|
@ -121,7 +121,7 @@ func glob(fs FileSystem, pattern string, hasRecursive bool) (matches, dirs []str
|
||||||
return nil, nil, fmt.Errorf("unexpected error after glob: %s", err)
|
return nil, nil, fmt.Errorf("unexpected error after glob: %s", err)
|
||||||
} else if isDir {
|
} else if isDir {
|
||||||
if file == "**" {
|
if file == "**" {
|
||||||
recurseDirs, err := walkAllDirs(m)
|
recurseDirs, err := fs.ListDirsRecursive(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -166,27 +166,6 @@ func isWild(pattern string) bool {
|
||||||
return strings.ContainsAny(pattern, "*?[")
|
return strings.ContainsAny(pattern, "*?[")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a list of all directories under dir
|
|
||||||
func walkAllDirs(dir string) (dirs []string, err error) {
|
|
||||||
err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if info.Mode().IsDir() {
|
|
||||||
name := info.Name()
|
|
||||||
if name[0] == '.' && name != "." {
|
|
||||||
return filepath.SkipDir
|
|
||||||
}
|
|
||||||
|
|
||||||
dirs = append(dirs, path)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
return dirs, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filters the strings in matches based on the glob patterns in excludes. Hierarchical (a/*) and
|
// Filters the strings in matches based on the glob patterns in excludes. Hierarchical (a/*) and
|
||||||
// recursive (**) glob patterns are supported.
|
// recursive (**) glob patterns are supported.
|
||||||
func filterExcludes(matches []string, excludes []string) ([]string, error) {
|
func filterExcludes(matches []string, excludes []string) ([]string, error) {
|
||||||
|
|
|
@ -18,18 +18,21 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var pwd, _ = os.Getwd()
|
var pwd, _ = os.Getwd()
|
||||||
|
|
||||||
var globTestCases = []struct {
|
type globTestCase struct {
|
||||||
pattern string
|
pattern string
|
||||||
matches []string
|
matches []string
|
||||||
excludes []string
|
excludes []string
|
||||||
deps []string
|
deps []string
|
||||||
err error
|
err error
|
||||||
}{
|
}
|
||||||
|
|
||||||
|
var globTestCases = []globTestCase{
|
||||||
// Current directory tests
|
// Current directory tests
|
||||||
{
|
{
|
||||||
pattern: "*",
|
pattern: "*",
|
||||||
|
@ -441,37 +444,75 @@ var globTestCases = []struct {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMockGlob(t *testing.T) {
|
||||||
|
files := []string{
|
||||||
|
"a/a/a",
|
||||||
|
"a/b/b",
|
||||||
|
"b/a",
|
||||||
|
"c/c",
|
||||||
|
"c/f/f.ext",
|
||||||
|
"c/g/g.ext",
|
||||||
|
"c/h/h",
|
||||||
|
"d.ext",
|
||||||
|
"e.ext",
|
||||||
|
".test/a",
|
||||||
|
".testing",
|
||||||
|
".test/.ing",
|
||||||
|
}
|
||||||
|
|
||||||
|
mockFiles := make(map[string][]byte)
|
||||||
|
|
||||||
|
for _, f := range files {
|
||||||
|
mockFiles[f] = nil
|
||||||
|
mockFiles[filepath.Join(pwd, "testdata", f)] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mock := MockFs(mockFiles)
|
||||||
|
|
||||||
|
for i, testCase := range globTestCases {
|
||||||
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
|
testGlob(t, mock, testCase)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGlob(t *testing.T) {
|
func TestGlob(t *testing.T) {
|
||||||
os.Chdir("testdata")
|
os.Chdir("testdata")
|
||||||
defer os.Chdir("..")
|
defer os.Chdir("..")
|
||||||
for _, testCase := range globTestCases {
|
for i, testCase := range globTestCases {
|
||||||
matches, deps, err := Glob(testCase.pattern, testCase.excludes)
|
t.Run(strconv.Itoa(i), func(t *testing.T) {
|
||||||
if err != testCase.err {
|
testGlob(t, OsFs, testCase)
|
||||||
t.Errorf(" pattern: %q", testCase.pattern)
|
})
|
||||||
if testCase.excludes != nil {
|
}
|
||||||
t.Errorf("excludes: %q", testCase.excludes)
|
}
|
||||||
}
|
|
||||||
t.Errorf(" error: %s", err)
|
func testGlob(t *testing.T, fs FileSystem, testCase globTestCase) {
|
||||||
continue
|
matches, deps, err := fs.Glob(testCase.pattern, testCase.excludes)
|
||||||
}
|
if err != testCase.err {
|
||||||
|
t.Errorf(" pattern: %q", testCase.pattern)
|
||||||
if !reflect.DeepEqual(matches, testCase.matches) {
|
if testCase.excludes != nil {
|
||||||
t.Errorf("incorrect matches list:")
|
t.Errorf("excludes: %q", testCase.excludes)
|
||||||
t.Errorf(" pattern: %q", testCase.pattern)
|
}
|
||||||
if testCase.excludes != nil {
|
t.Errorf(" error: %s", err)
|
||||||
t.Errorf("excludes: %q", testCase.excludes)
|
return
|
||||||
}
|
}
|
||||||
t.Errorf(" got: %#v", matches)
|
|
||||||
t.Errorf("expected: %#v", testCase.matches)
|
if !reflect.DeepEqual(matches, testCase.matches) {
|
||||||
}
|
t.Errorf("incorrect matches list:")
|
||||||
if !reflect.DeepEqual(deps, testCase.deps) {
|
t.Errorf(" pattern: %q", testCase.pattern)
|
||||||
t.Errorf("incorrect deps list:")
|
if testCase.excludes != nil {
|
||||||
t.Errorf(" pattern: %q", testCase.pattern)
|
t.Errorf("excludes: %q", testCase.excludes)
|
||||||
if testCase.excludes != nil {
|
}
|
||||||
t.Errorf("excludes: %q", testCase.excludes)
|
t.Errorf(" got: %#v", matches)
|
||||||
}
|
t.Errorf("expected: %#v", testCase.matches)
|
||||||
t.Errorf(" got: %#v", deps)
|
}
|
||||||
t.Errorf("expected: %#v", testCase.deps)
|
if !reflect.DeepEqual(deps, testCase.deps) {
|
||||||
}
|
t.Errorf("incorrect deps list:")
|
||||||
|
t.Errorf(" pattern: %q", testCase.pattern)
|
||||||
|
if testCase.excludes != nil {
|
||||||
|
t.Errorf("excludes: %q", testCase.excludes)
|
||||||
|
}
|
||||||
|
t.Errorf(" got: %#v", deps)
|
||||||
|
t.Errorf("expected: %#v", testCase.deps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue