// Copyright 2009 The Go Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file. package filepath_test import ( "errors" "io/ioutil" "os" "path/filepath" "reflect" "runtime" "strings" "testing") var supportsSymlinks = true type PathTest struct { path, result string} var cleantests = []PathTest{ // Already clean {"abc", "abc"}, {"abc/def", "abc/def"}, {"a/b/c", "a/b/c"}, {".", "."}, {"..", ".."}, {"../..", "../.."}, {"../../abc", "../../abc"}, {"/abc", "/abc"}, {"/", "/"}, // Empty is current dir {"", "."}, // Remove trailing slash {"abc/", "abc"}, {"abc/def/", "abc/def"}, {"a/b/c/", "a/b/c"}, {"./", "."}, {"../", ".."}, {"../../", "../.."}, {"/abc/", "/abc"}, // Remove doubled slash {"abc//def//ghi", "abc/def/ghi"}, {"//abc", "/abc"}, {"///abc", "/abc"}, {"//abc//", "/abc"}, {"abc//", "abc"}, // Remove . elements {"abc/./def", "abc/def"}, {"/./abc/def", "/abc/def"}, {"abc/.", "abc"}, // Remove .. elements {"abc/def/ghi/../jkl", "abc/def/jkl"}, {"abc/def/../ghi/../jkl", "abc/jkl"}, {"abc/def/..", "abc"}, {"abc/def/../..", "."}, {"/abc/def/../..", "/"}, {"abc/def/../../..", ".."}, {"/abc/def/../../..", "/"}, {"abc/def/../../../ghi/jkl/../../../mno", "../../mno"}, {"/../abc", "/abc"}, // Combinations {"abc/./../def", "def"}, {"abc//./../def", "def"}, {"abc/../../././../def", "../../def"},} var wincleantests = []PathTest{ {`c:`, `c:.`}, {`c:\`, `c:\`}, {`c:\abc`, `c:\abc`}, {`c:abc\..\..\.\.\..\def`, `c:..\..\def`}, {`c:\abc\def\..\..`, `c:\`}, {`c:\..\abc`, `c:\abc`}, {`c:..\abc`, `c:..\abc`}, {`\`, `\`}, {`/`, `\`}, {`\\i\..\c$`, `\c$`}, {`\\i\..\i\c$`, `\i\c$`}, {`\\i\..\I\c$`, `\I\c$`}, {`\\host\share\foo\..\bar`, `\\host\share\bar`}, {`//host/share/foo/../baz`, `\\host\share\baz`}, {`\\a\b\..\c`, `\\a\b\c`}, {`\\a\b`, `\\a\b`},} func TestClean(t *testing.T) { tests := cleantests if runtime.GOOS == "windows" { for i := range tests { tests[i].result = filepath.FromSlash(tests[i].result) } tests = append(tests, wincleantests...) } for _, test := range tests { if s := filepath.Clean(test.path); s != test.result { t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result) } if s := filepath.Clean(test.result); s != test.result { t.Errorf("Clean(%q) = %q, want %q", test.result, s, test.result) } } if testing.Short() { t.Skip("skipping malloc count in short mode") } if runtime.GOMAXPROCS(0) > 1 { t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1") return } for _, test := range tests { allocs := testing.AllocsPerRun(100, func() { filepath.Clean(test.result) }) if allocs > 0 { t.Errorf("Clean(%q): %v allocs, want zero", test.result, allocs) } }} const sep = filepath.Separator var slashtests = []PathTest{ {"", ""}, {"/", string(sep)}, {"/a/b", string([]byte{sep, 'a', sep, 'b'})}, {"a//b", string([]byte{'a', sep, sep, 'b'})},} func TestFromAndToSlash(t *testing.T) { for _, test := range slashtests { if s := filepath.FromSlash(test.path); s != test.result { t.Errorf("FromSlash(%q) = %q, want %q", test.path, s, test.result) } if s := filepath.ToSlash(test.result); s != test.path { t.Errorf("ToSlash(%q) = %q, want %q", test.result, s, test.path) } }} type SplitListTest struct { list string result []string} const lsep = filepath.ListSeparator var splitlisttests = []SplitListTest{ {"", []string{}}, {string([]byte{'a', lsep, 'b'}), []string{"a", "b"}}, {string([]byte{lsep, 'a', lsep, 'b'}), []string{"", "a", "b"}},} var winsplitlisttests = []SplitListTest{ // quoted {`"a"`, []string{`a`}}, // semicolon {`";"`, []string{`;`}}, {`"a;b"`, []string{`a;b`}}, {`";";`, []string{`;`, ``}}, {`;";"`, []string{``, `;`}}, // partially quoted {`a";"b`, []string{`a;b`}}, {`a; ""b`, []string{`a`, ` b`}}, {`"a;b`, []string{`a;b`}}, {`""a;b`, []string{`a`, `b`}}, {`"""a;b`, []string{`a;b`}}, {`""""a;b`, []string{`a`, `b`}}, {`a";b`, []string{`a;b`}}, {`a;b";c`, []string{`a`, `b;c`}}, {`"a";b";c`, []string{`a`, `b;c`}},} func TestSplitList(t *testing.T) { tests := splitlisttests if runtime.GOOS == "windows" { tests = append(tests, winsplitlisttests...) } for _, test := range tests { if l := filepath.SplitList(test.list); !reflect.DeepEqual(l, test.result) { t.Errorf("SplitList(%#q) = %#q, want %#q", test.list, l, test.result) } }} type SplitTest struct { path, dir, file string} var unixsplittests = []SplitTest{ {"a/b", "a/", "b"}, {"a/b/", "a/b/", ""}, {"a/", "a/", ""}, {"a", "", "a"}, {"/", "/", ""},} var winsplittests = []SplitTest{ {`c:`, `c:`, ``}, {`c:/`, `c:/`, ``}, {`c:/foo`, `c:/`, `foo`}, {`c:/foo/bar`, `c:/foo/`, `bar`}, {`//host/share`, `//host/share`, ``}, {`//host/share/`, `//host/share/`, ``}, {`//host/share/foo`, `//host/share/`, `foo`}, {`\\host\share`, `\\host\share`, ``}, {`\\host\share\`, `\\host\share\`, ``}, {`\\host\share\foo`, `\\host\share\`, `foo`},} func TestSplit(t *testing.T) { var splittests []SplitTest splittests = unixsplittests if runtime.GOOS == "windows" { splittests = append(splittests, winsplittests...) } for _, test := range splittests { if d, f := filepath.Split(test.path); d != test.dir || f != test.file { t.Errorf("Split(%q) = %q, %q, want %q, %q", test.path, d, f, test.dir, test.file) } }} type JoinTest struct { elem []string path string} var jointests = []JoinTest{ // zero parameters {[]string{}, ""}, // one parameter {[]string{""}, ""}, {[]string{"/"}, "/"}, {[]string{"a"}, "a"}, // two parameters {[]string{"a", "b"}, "a/b"}, {[]string{"a", ""}, "a"}, {[]string{"", "b"}, "b"}, {[]string{"/", "a"}, "/a"}, {[]string{"/", "a/b"}, "/a/b"}, {[]string{"/", ""}, "/"}, {[]string{"//", "a"}, "/a"}, {[]string{"/a", "b"}, "/a/b"}, {[]string{"a/", "b"}, "a/b"}, {[]string{"a/", ""}, "a"}, {[]string{"", ""}, ""}, // three parameters {[]string{"/", "a", "b"}, "/a/b"},} var winjointests = []JoinTest{ {[]string{`directory`, `file`}, `directory\file`}, {[]string{`C:\Windows\`, `System32`}, `C:\Windows\System32`}, {[]string{`C:\Windows\`, ``}, `C:\Windows`}, {[]string{`C:\`, `Windows`}, `C:\Windows`}, {[]string{`C:`, `a`}, `C:a`}, {[]string{`C:`, `a\b`}, `C:a\b`}, {[]string{`C:`, `a`, `b`}, `C:a\b`}, {[]string{`C:.`, `a`}, `C:a`}, {[]string{`C:a`, `b`}, `C:a\b`}, {[]string{`C:a`, `b`, `d`}, `C:a\b\d`}, {[]string{`\\host\share`, `foo`}, `\\host\share\foo`}, {[]string{`\\host\share\foo`}, `\\host\share\foo`}, {[]string{`//host/share`, `foo/bar`}, `\\host\share\foo\bar`}, {[]string{`\`}, `\`}, {[]string{`\`, ``}, `\`}, {[]string{`\`, `a`}, `\a`}, {[]string{`\\`, `a`}, `\a`}, {[]string{`\`, `a`, `b`}, `\a\b`}, {[]string{`\\`, `a`, `b`}, `\a\b`}, {[]string{`\`, `\\a\b`, `c`}, `\a\b\c`}, {[]string{`\\a`, `b`, `c`}, `\a\b\c`}, {[]string{`\\a\`, `b`, `c`}, `\a\b\c`},} func TestJoin(t *testing.T) { if runtime.GOOS == "windows" { jointests = append(jointests, winjointests...) } for _, test := range jointests { expected := filepath.FromSlash(test.path) if p := filepath.Join(test.elem...); p != expected { t.Errorf("join(%q) = %q, want %q", test.elem, p, expected) } }} type ExtTest struct { path, ext string} var exttests = []ExtTest{ {"path.go", ".go"}, {"path.pb.go", ".go"}, {"a.dir/b", ""}, {"a.dir/b.go", ".go"}, {"a.dir/", ""},} func TestExt(t *testing.T) { for _, test := range exttests { if x := filepath.Ext(test.path); x != test.ext { t.Errorf("Ext(%q) = %q, want %q", test.path, x, test.ext) } }} type Node struct { name string entries []*Node // nil if the entry is a file mark int} var tree = &Node{ "testdata", []*Node{ {"a", nil, 0}, {"b", []*Node{}, 0}, {"c", nil, 0}, { "d", []*Node{ {"x", nil, 0}, {"y", []*Node{}, 0}, { "z", []*Node{ {"u", nil, 0}, {"v", nil, 0}, }, 0, }, }, 0, }, }, 0,} func walkTree(n *Node, path string, f func(path string, n *Node)) { f(path, n) for _, e := range n.entries { walkTree(e, filepath.Join(path, e.name), f) }} func makeTree(t *testing.T) { walkTree(tree, tree.name, func(path string, n *Node) { if n.entries == nil { fd, err := os.Create(path) if err != nil { t.Errorf("makeTree: %v", err) return } fd.Close() } else { os.Mkdir(path, 0770) } })} func markTree(n *Node) { walkTree(n, "", func(path string, n *Node) { n.mark++ }) } func checkMarks(t *testing.T, report bool) { walkTree(tree, tree.name, func(path string, n *Node) { if n.mark != 1 && report { t.Errorf("node %s mark = %d; expected 1", path, n.mark) } n.mark = 0 })} // Assumes that each node name is unique. Good enough for a test.// If clear is true, any incoming error is cleared before return. The errors// are always accumulated, though.func mark(path string, info os.FileInfo, err error, errors *[]error, clear bool) error { if err != nil { *errors = append(*errors, err) if clear { return nil } return err } name := info.Name() walkTree(tree, tree.name, func(path string, n *Node) { if n.name == name { n.mark++ } }) return nil} func chtmpdir(t *testing.T) (restore func()) { oldwd, err := os.Getwd() if err != nil { t.Fatalf("chtmpdir: %v", err) } d, err := ioutil.TempDir("", "test") if err != nil { t.Fatalf("chtmpdir: %v", err) } if err := os.Chdir(d); err != nil { t.Fatalf("chtmpdir: %v", err) } return func() { if err := os.Chdir(oldwd); err != nil { t.Fatalf("chtmpdir: %v", err) } os.RemoveAll(d) }} func TestWalk(t *testing.T) { if runtime.GOOS == "darwin" { switch runtime.GOARCH { case "arm", "arm64": restore := chtmpdir(t) defer restore() } } makeTree(t) errors := make([]error, 0, 10) clear := true markFn := func(path string, info os.FileInfo, err error) error { return mark(path, info, err, &errors, clear) } // Expect no errors. err := filepath.Walk(tree.name, markFn) if err != nil { t.Fatalf("no error expected, found: %s", err) } if len(errors) != 0 { t.Fatalf("unexpected errors: %s", errors) } checkMarks(t, true) errors = errors[0:0] // Test permission errors. Only possible if we're not root // and only on some file systems (AFS, FAT). To avoid errors during // all.bash on those file systems, skip during go test -short. if os.Getuid() > 0 && !testing.Short() { // introduce 2 errors: chmod top-level directories to 0 os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0) os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0) // 3) capture errors, expect two. // mark respective subtrees manually markTree(tree.entries[1]) markTree(tree.entries[3]) // correct double-marking of directory itself tree.entries[1].mark-- tree.entries[3].mark-- err := filepath.Walk(tree.name, markFn) if err != nil { t.Fatalf("expected no error return from Walk, got %s", err) } if len(errors) != 2 { t.Errorf("expected 2 errors, got %d: %s", len(errors), errors) } // the inaccessible subtrees were marked manually checkMarks(t, true) errors = errors[0:0] // 4) capture errors, stop after first error. // mark respective subtrees manually markTree(tree.entries[1]) markTree(tree.entries[3]) // correct double-marking of directory itself tree.entries[1].mark-- tree.entries[3].mark-- clear = false // error will stop processing err = filepath.Walk(tree.name, markFn) if err == nil { t.Fatalf("expected error return from Walk") } if len(errors) != 1 { t.Errorf("expected 1 error, got %d: %s", len(errors), errors) } // the inaccessible subtrees were marked manually checkMarks(t, false) errors = errors[0:0] // restore permissions os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770) os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770) }Lines 1–500 of 1170. Load more
// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package filepath_test import ( "errors" "io/ioutil" "os" "path/filepath" "reflect" "runtime" "strings" "testing" ) var supportsSymlinks = true type PathTest struct { path, result string } var cleantests = []PathTest{ // Already clean {"abc", "abc"}, {"abc/def", "abc/def"}, {"a/b/c", "a/b/c"}, {".", "."}, {"..", ".."}, {"../..", "../.."}, {"../../abc", "../../abc"}, {"/abc", "/abc"}, {"/", "/"}, // Empty is current dir {"", "."}, // Remove trailing slash {"abc/", "abc"}, {"abc/def/", "abc/def"}, {"a/b/c/", "a/b/c"}, {"./", "."}, {"../", ".."}, {"../../", "../.."}, {"/abc/", "/abc"}, // Remove doubled slash {"abc//def//ghi", "abc/def/ghi"}, {"//abc", "/abc"}, {"///abc", "/abc"}, {"//abc//", "/abc"}, {"abc//", "abc"}, // Remove . elements {"abc/./def", "abc/def"}, {"/./abc/def", "/abc/def"}, {"abc/.", "abc"}, // Remove .. elements {"abc/def/ghi/../jkl", "abc/def/jkl"}, {"abc/def/../ghi/../jkl", "abc/jkl"}, {"abc/def/..", "abc"}, {"abc/def/../..", "."}, {"/abc/def/../..", "/"}, {"abc/def/../../..", ".."}, {"/abc/def/../../..", "/"}, {"abc/def/../../../ghi/jkl/../../../mno", "../../mno"}, {"/../abc", "/abc"}, // Combinations {"abc/./../def", "def"}, {"abc//./../def", "def"}, {"abc/../../././../def", "../../def"}, } var wincleantests = []PathTest{ {`c:`, `c:.`}, {`c:\`, `c:\`}, {`c:\abc`, `c:\abc`}, {`c:abc\..\..\.\.\..\def`, `c:..\..\def`}, {`c:\abc\def\..\..`, `c:\`}, {`c:\..\abc`, `c:\abc`}, {`c:..\abc`, `c:..\abc`}, {`\`, `\`}, {`/`, `\`}, {`\\i\..\c$`, `\c$`}, {`\\i\..\i\c$`, `\i\c$`}, {`\\i\..\I\c$`, `\I\c$`}, {`\\host\share\foo\..\bar`, `\\host\share\bar`}, {`//host/share/foo/../baz`, `\\host\share\baz`}, {`\\a\b\..\c`, `\\a\b\c`}, {`\\a\b`, `\\a\b`}, } func TestClean(t *testing.T) { tests := cleantests if runtime.GOOS == "windows" { for i := range tests { tests[i].result = filepath.FromSlash(tests[i].result) } tests = append(tests, wincleantests...) } for _, test := range tests { if s := filepath.Clean(test.path); s != test.result { t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result) } if s := filepath.Clean(test.result); s != test.result { t.Errorf("Clean(%q) = %q, want %q", test.result, s, test.result) } } if testing.Short() { t.Skip("skipping malloc count in short mode") } if runtime.GOMAXPROCS(0) > 1 { t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1") return } for _, test := range tests { allocs := testing.AllocsPerRun(100, func() { filepath.Clean(test.result) }) if allocs > 0 { t.Errorf("Clean(%q): %v allocs, want zero", test.result, allocs) } } } const sep = filepath.Separator var slashtests = []PathTest{ {"", ""}, {"/", string(sep)}, {"/a/b", string([]byte{sep, 'a', sep, 'b'})}, {"a//b", string([]byte{'a', sep, sep, 'b'})}, } func TestFromAndToSlash(t *testing.T) { for _, test := range slashtests { if s := filepath.FromSlash(test.path); s != test.result { t.Errorf("FromSlash(%q) = %q, want %q", test.path, s, test.result) } if s := filepath.ToSlash(test.result); s != test.path { t.Errorf("ToSlash(%q) = %q, want %q", test.result, s, test.path) } } } type SplitListTest struct { list string result []string } const lsep = filepath.ListSeparator var splitlisttests = []SplitListTest{ {"", []string{}}, {string([]byte{'a', lsep, 'b'}), []string{"a", "b"}}, {string([]byte{lsep, 'a', lsep, 'b'}), []string{"", "a", "b"}}, } var winsplitlisttests = []SplitListTest{ // quoted {`"a"`, []string{`a`}}, // semicolon {`";"`, []string{`;`}}, {`"a;b"`, []string{`a;b`}}, {`";";`, []string{`;`, ``}}, {`;";"`, []string{``, `;`}}, // partially quoted {`a";"b`, []string{`a;b`}}, {`a; ""b`, []string{`a`, ` b`}}, {`"a;b`, []string{`a;b`}}, {`""a;b`, []string{`a`, `b`}}, {`"""a;b`, []string{`a;b`}}, {`""""a;b`, []string{`a`, `b`}}, {`a";b`, []string{`a;b`}}, {`a;b";c`, []string{`a`, `b;c`}}, {`"a";b";c`, []string{`a`, `b;c`}}, } func TestSplitList(t *testing.T) { tests := splitlisttests if runtime.GOOS == "windows" { tests = append(tests, winsplitlisttests...) } for _, test := range tests { if l := filepath.SplitList(test.list); !reflect.DeepEqual(l, test.result) { t.Errorf("SplitList(%#q) = %#q, want %#q", test.list, l, test.result) } } } type SplitTest struct { path, dir, file string } var unixsplittests = []SplitTest{ {"a/b", "a/", "b"}, {"a/b/", "a/b/", ""}, {"a/", "a/", ""}, {"a", "", "a"}, {"/", "/", ""}, } var winsplittests = []SplitTest{ {`c:`, `c:`, ``}, {`c:/`, `c:/`, ``}, {`c:/foo`, `c:/`, `foo`}, {`c:/foo/bar`, `c:/foo/`, `bar`}, {`//host/share`, `//host/share`, ``}, {`//host/share/`, `//host/share/`, ``}, {`//host/share/foo`, `//host/share/`, `foo`}, {`\\host\share`, `\\host\share`, ``}, {`\\host\share\`, `\\host\share\`, ``}, {`\\host\share\foo`, `\\host\share\`, `foo`}, } func TestSplit(t *testing.T) { var splittests []SplitTest splittests = unixsplittests if runtime.GOOS == "windows" { splittests = append(splittests, winsplittests...) } for _, test := range splittests { if d, f := filepath.Split(test.path); d != test.dir || f != test.file { t.Errorf("Split(%q) = %q, %q, want %q, %q", test.path, d, f, test.dir, test.file) } } } type JoinTest struct { elem []string path string } var jointests = []JoinTest{ // zero parameters {[]string{}, ""}, // one parameter {[]string{""}, ""}, {[]string{"/"}, "/"}, {[]string{"a"}, "a"}, // two parameters {[]string{"a", "b"}, "a/b"}, {[]string{"a", ""}, "a"}, {[]string{"", "b"}, "b"}, {[]string{"/", "a"}, "/a"}, {[]string{"/", "a/b"}, "/a/b"}, {[]string{"/", ""}, "/"}, {[]string{"//", "a"}, "/a"}, {[]string{"/a", "b"}, "/a/b"}, {[]string{"a/", "b"}, "a/b"}, {[]string{"a/", ""}, "a"}, {[]string{"", ""}, ""}, // three parameters {[]string{"/", "a", "b"}, "/a/b"}, } var winjointests = []JoinTest{ {[]string{`directory`, `file`}, `directory\file`}, {[]string{`C:\Windows\`, `System32`}, `C:\Windows\System32`}, {[]string{`C:\Windows\`, ``}, `C:\Windows`}, {[]string{`C:\`, `Windows`}, `C:\Windows`}, {[]string{`C:`, `a`}, `C:a`}, {[]string{`C:`, `a\b`}, `C:a\b`}, {[]string{`C:`, `a`, `b`}, `C:a\b`}, {[]string{`C:.`, `a`}, `C:a`}, {[]string{`C:a`, `b`}, `C:a\b`}, {[]string{`C:a`, `b`, `d`}, `C:a\b\d`}, {[]string{`\\host\share`, `foo`}, `\\host\share\foo`}, {[]string{`\\host\share\foo`}, `\\host\share\foo`}, {[]string{`//host/share`, `foo/bar`}, `\\host\share\foo\bar`}, {[]string{`\`}, `\`}, {[]string{`\`, ``}, `\`}, {[]string{`\`, `a`}, `\a`}, {[]string{`\\`, `a`}, `\a`}, {[]string{`\`, `a`, `b`}, `\a\b`}, {[]string{`\\`, `a`, `b`}, `\a\b`}, {[]string{`\`, `\\a\b`, `c`}, `\a\b\c`}, {[]string{`\\a`, `b`, `c`}, `\a\b\c`}, {[]string{`\\a\`, `b`, `c`}, `\a\b\c`}, } func TestJoin(t *testing.T) { if runtime.GOOS == "windows" { jointests = append(jointests, winjointests...) } for _, test := range jointests { expected := filepath.FromSlash(test.path) if p := filepath.Join(test.elem...); p != expected { t.Errorf("join(%q) = %q, want %q", test.elem, p, expected) } } } type ExtTest struct { path, ext string } var exttests = []ExtTest{ {"path.go", ".go"}, {"path.pb.go", ".go"}, {"a.dir/b", ""}, {"a.dir/b.go", ".go"}, {"a.dir/", ""}, } func TestExt(t *testing.T) { for _, test := range exttests { if x := filepath.Ext(test.path); x != test.ext { t.Errorf("Ext(%q) = %q, want %q", test.path, x, test.ext) } } } type Node struct { name string entries []*Node // nil if the entry is a file mark int } var tree = &Node{ "testdata", []*Node{ {"a", nil, 0}, {"b", []*Node{}, 0}, {"c", nil, 0}, { "d", []*Node{ {"x", nil, 0}, {"y", []*Node{}, 0}, { "z", []*Node{ {"u", nil, 0}, {"v", nil, 0}, }, 0, }, }, 0, }, }, 0, } func walkTree(n *Node, path string, f func(path string, n *Node)) { f(path, n) for _, e := range n.entries { walkTree(e, filepath.Join(path, e.name), f) } } func makeTree(t *testing.T) { walkTree(tree, tree.name, func(path string, n *Node) { if n.entries == nil { fd, err := os.Create(path) if err != nil { t.Errorf("makeTree: %v", err) return } fd.Close() } else { os.Mkdir(path, 0770) } }) } func markTree(n *Node) { walkTree(n, "", func(path string, n *Node) { n.mark++ }) } func checkMarks(t *testing.T, report bool) { walkTree(tree, tree.name, func(path string, n *Node) { if n.mark != 1 && report { t.Errorf("node %s mark = %d; expected 1", path, n.mark) } n.mark = 0 }) } // Assumes that each node name is unique. Good enough for a test. // If clear is true, any incoming error is cleared before return. The errors // are always accumulated, though. func mark(path string, info os.FileInfo, err error, errors *[]error, clear bool) error { if err != nil { *errors = append(*errors, err) if clear { return nil } return err } name := info.Name() walkTree(tree, tree.name, func(path string, n *Node) { if n.name == name { n.mark++ } }) return nil } func chtmpdir(t *testing.T) (restore func()) { oldwd, err := os.Getwd() if err != nil { t.Fatalf("chtmpdir: %v", err) } d, err := ioutil.TempDir("", "test") if err != nil { t.Fatalf("chtmpdir: %v", err) } if err := os.Chdir(d); err != nil { t.Fatalf("chtmpdir: %v", err) } return func() { if err := os.Chdir(oldwd); err != nil { t.Fatalf("chtmpdir: %v", err) } os.RemoveAll(d) } } func TestWalk(t *testing.T) { if runtime.GOOS == "darwin" { switch runtime.GOARCH { case "arm", "arm64": restore := chtmpdir(t) defer restore() } } makeTree(t) errors := make([]error, 0, 10) clear := true markFn := func(path string, info os.FileInfo, err error) error { return mark(path, info, err, &errors, clear) } // Expect no errors. err := filepath.Walk(tree.name, markFn) if err != nil { t.Fatalf("no error expected, found: %s", err) } if len(errors) != 0 { t.Fatalf("unexpected errors: %s", errors) } checkMarks(t, true) errors = errors[0:0] // Test permission errors. Only possible if we're not root // and only on some file systems (AFS, FAT). To avoid errors during // all.bash on those file systems, skip during go test -short. if os.Getuid() > 0 && !testing.Short() { // introduce 2 errors: chmod top-level directories to 0 os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0) os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0) // 3) capture errors, expect two. // mark respective subtrees manually markTree(tree.entries[1]) markTree(tree.entries[3]) // correct double-marking of directory itself tree.entries[1].mark-- tree.entries[3].mark-- err := filepath.Walk(tree.name, markFn) if err != nil { t.Fatalf("expected no error return from Walk, got %s", err) } if len(errors) != 2 { t.Errorf("expected 2 errors, got %d: %s", len(errors), errors) } // the inaccessible subtrees were marked manually checkMarks(t, true) errors = errors[0:0] // 4) capture errors, stop after first error. // mark respective subtrees manually markTree(tree.entries[1]) markTree(tree.entries[3]) // correct double-marking of directory itself tree.entries[1].mark-- tree.entries[3].mark-- clear = false // error will stop processing err = filepath.Walk(tree.name, markFn) if err == nil { t.Fatalf("expected error return from Walk") } if len(errors) != 1 { t.Errorf("expected 1 error, got %d: %s", len(errors), errors) } // the inaccessible subtrees were marked manually checkMarks(t, false) errors = errors[0:0] // restore permissions os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770) os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770) }