aboutsummaryrefslogtreecommitdiff
path: root/src/testing
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2020-07-06 11:27:12 -0400
committerRuss Cox <rsc@golang.org>2020-10-20 17:53:14 +0000
commitb64202bc29b9c1cf0118878d1c0acc9cdb2308f6 (patch)
tree6c632cefefa5dbb996d04f57e7a6e2282ffae4bb /src/testing
parent7a131acfd142f0fc7612365078b9f00e371fc0e2 (diff)
downloadgo-b64202bc29b9c1cf0118878d1c0acc9cdb2308f6.tar.gz
go-b64202bc29b9c1cf0118878d1c0acc9cdb2308f6.zip
io/fs: add Glob and GlobFS
Add Glob helper function, GlobFS interface, and test. Add Glob method to fstest.MapFS. Add testing of Glob method to fstest.TestFS. For #41190. Change-Id: If89dd7f63e310ba5ca2651340267a9ff39fcc0c7 Reviewed-on: https://go-review.googlesource.com/c/go/+/243915 Trust: Russ Cox <rsc@golang.org> Reviewed-by: Rob Pike <r@golang.org>
Diffstat (limited to 'src/testing')
-rw-r--r--src/testing/fstest/mapfs.go4
-rw-r--r--src/testing/fstest/testfs.go96
2 files changed, 100 insertions, 0 deletions
diff --git a/src/testing/fstest/mapfs.go b/src/testing/fstest/mapfs.go
index 1eaf8f0040..10e56f5b3c 100644
--- a/src/testing/fstest/mapfs.go
+++ b/src/testing/fstest/mapfs.go
@@ -128,6 +128,10 @@ func (fsys MapFS) ReadDir(name string) ([]fs.DirEntry, error) {
return fs.ReadDir(fsOnly{fsys}, name)
}
+func (fsys MapFS) Glob(pattern string) ([]string, error) {
+ return fs.Glob(fsOnly{fsys}, pattern)
+}
+
// A mapFileInfo implements fs.FileInfo and fs.DirEntry for a given map file.
type mapFileInfo struct {
name string
diff --git a/src/testing/fstest/testfs.go b/src/testing/fstest/testfs.go
index 4ea6ed6095..21cd00e5b6 100644
--- a/src/testing/fstest/testfs.go
+++ b/src/testing/fstest/testfs.go
@@ -11,6 +11,8 @@ import (
"io"
"io/fs"
"io/ioutil"
+ "path"
+ "reflect"
"sort"
"strings"
"testing/iotest"
@@ -226,6 +228,8 @@ func (t *fsTester) checkDir(dir string) {
t.errorf("%s: fs.ReadDir: list not sorted: %s before %s", dir, list2[i].Name(), list2[i+1].Name())
}
}
+
+ t.checkGlob(dir, list)
}
// formatEntry formats an fs.DirEntry into a string for error messages and comparison.
@@ -243,6 +247,98 @@ func formatInfo(info fs.FileInfo) string {
return fmt.Sprintf("%s IsDir=%v Mode=%v Size=%d ModTime=%v", info.Name(), info.IsDir(), info.Mode(), info.Size(), info.ModTime())
}
+// checkGlob checks that various glob patterns work if the file system implements GlobFS.
+func (t *fsTester) checkGlob(dir string, list []fs.DirEntry) {
+ if _, ok := t.fsys.(fs.GlobFS); !ok {
+ return
+ }
+
+ // Make a complex glob pattern prefix that only matches dir.
+ var glob string
+ if dir != "." {
+ elem := strings.Split(dir, "/")
+ for i, e := range elem {
+ var pattern []rune
+ for j, r := range e {
+ if r == '*' || r == '?' || r == '\\' || r == '[' {
+ pattern = append(pattern, '\\', r)
+ continue
+ }
+ switch (i + j) % 5 {
+ case 0:
+ pattern = append(pattern, r)
+ case 1:
+ pattern = append(pattern, '[', r, ']')
+ case 2:
+ pattern = append(pattern, '[', r, '-', r, ']')
+ case 3:
+ pattern = append(pattern, '[', '\\', r, ']')
+ case 4:
+ pattern = append(pattern, '[', '\\', r, '-', '\\', r, ']')
+ }
+ }
+ elem[i] = string(pattern)
+ }
+ glob = strings.Join(elem, "/") + "/"
+ }
+
+ // Try to find a letter that appears in only some of the final names.
+ c := rune('a')
+ for ; c <= 'z'; c++ {
+ have, haveNot := false, false
+ for _, d := range list {
+ if strings.ContainsRune(d.Name(), c) {
+ have = true
+ } else {
+ haveNot = true
+ }
+ }
+ if have && haveNot {
+ break
+ }
+ }
+ if c > 'z' {
+ c = 'a'
+ }
+ glob += "*" + string(c) + "*"
+
+ var want []string
+ for _, d := range list {
+ if strings.ContainsRune(d.Name(), c) {
+ want = append(want, path.Join(dir, d.Name()))
+ }
+ }
+
+ names, err := t.fsys.(fs.GlobFS).Glob(glob)
+ if err != nil {
+ t.errorf("%s: Glob(%#q): %v", dir, glob, err)
+ return
+ }
+ if reflect.DeepEqual(want, names) {
+ return
+ }
+
+ if !sort.StringsAreSorted(names) {
+ t.errorf("%s: Glob(%#q): unsorted output:\n%s", dir, glob, strings.Join(names, "\n"))
+ sort.Strings(names)
+ }
+
+ var problems []string
+ for len(want) > 0 || len(names) > 0 {
+ switch {
+ case len(want) > 0 && len(names) > 0 && want[0] == names[0]:
+ want, names = want[1:], names[1:]
+ case len(want) > 0 && (len(names) == 0 || want[0] < names[0]):
+ problems = append(problems, "missing: "+want[0])
+ want = want[1:]
+ default:
+ problems = append(problems, "extra: "+names[0])
+ names = names[1:]
+ }
+ }
+ t.errorf("%s: Glob(%#q): wrong output:\n%s", dir, glob, strings.Join(problems, "\n"))
+}
+
// checkStat checks that a direct stat of path matches entry,
// which was found in the parent's directory listing.
func (t *fsTester) checkStat(path string, entry fs.DirEntry) {