| // 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 path | |
| import ( | |
| "os" | |
| "testing" | |
| ) | |
| type CleanTest struct { | |
| path, clean string | |
| } | |
| var cleantests = []CleanTest{ | |
| // Already clean | |
| CleanTest{"", "."}, | |
| CleanTest{"abc", "abc"}, | |
| CleanTest{"abc/def", "abc/def"}, | |
| CleanTest{"a/b/c", "a/b/c"}, | |
| CleanTest{".", "."}, | |
| CleanTest{"..", ".."}, | |
| CleanTest{"../..", "../.."}, | |
| CleanTest{"../../abc", "../../abc"}, | |
| CleanTest{"/abc", "/abc"}, | |
| CleanTest{"/", "/"}, | |
| // Remove trailing slash | |
| CleanTest{"abc/", "abc"}, | |
| CleanTest{"abc/def/", "abc/def"}, | |
| CleanTest{"a/b/c/", "a/b/c"}, | |
| CleanTest{"./", "."}, | |
| CleanTest{"../", ".."}, | |
| CleanTest{"../../", "../.."}, | |
| CleanTest{"/abc/", "/abc"}, | |
| // Remove doubled slash | |
| CleanTest{"abc//def//ghi", "abc/def/ghi"}, | |
| CleanTest{"//abc", "/abc"}, | |
| CleanTest{"///abc", "/abc"}, | |
| CleanTest{"//abc//", "/abc"}, | |
| CleanTest{"abc//", "abc"}, | |
| // Remove . elements | |
| CleanTest{"abc/./def", "abc/def"}, | |
| CleanTest{"/./abc/def", "/abc/def"}, | |
| CleanTest{"abc/.", "abc"}, | |
| // Remove .. elements | |
| CleanTest{"abc/def/ghi/../jkl", "abc/def/jkl"}, | |
| CleanTest{"abc/def/../ghi/../jkl", "abc/jkl"}, | |
| CleanTest{"abc/def/..", "abc"}, | |
| CleanTest{"abc/def/../..", "."}, | |
| CleanTest{"/abc/def/../..", "/"}, | |
| CleanTest{"abc/def/../../..", ".."}, | |
| CleanTest{"/abc/def/../../..", "/"}, | |
| CleanTest{"abc/def/../../../ghi/jkl/../../../mno", "../../mno"}, | |
| // Combinations | |
| CleanTest{"abc/./../def", "def"}, | |
| CleanTest{"abc//./../def", "def"}, | |
| CleanTest{"abc/../../././../def", "../../def"}, | |
| } | |
| func TestClean(t *testing.T) { | |
| for _, test := range cleantests { | |
| if s := Clean(test.path); s != test.clean { | |
| t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.clean) | |
| } | |
| } | |
| } | |
| type SplitTest struct { | |
| path, dir, file string | |
| } | |
| var splittests = []SplitTest{ | |
| SplitTest{"a/b", "a/", "b"}, | |
| SplitTest{"a/b/", "a/b/", ""}, | |
| SplitTest{"a/", "a/", ""}, | |
| SplitTest{"a", "", "a"}, | |
| SplitTest{"/", "/", ""}, | |
| } | |
| func TestSplit(t *testing.T) { | |
| for _, test := range splittests { | |
| if d, f := 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 | |
| JoinTest{[]string{}, ""}, | |
| // one parameter | |
| JoinTest{[]string{""}, ""}, | |
| JoinTest{[]string{"a"}, "a"}, | |
| // two parameters | |
| JoinTest{[]string{"a", "b"}, "a/b"}, | |
| JoinTest{[]string{"a", ""}, "a"}, | |
| JoinTest{[]string{"", "b"}, "b"}, | |
| JoinTest{[]string{"/", "a"}, "/a"}, | |
| JoinTest{[]string{"/", ""}, "/"}, | |
| JoinTest{[]string{"a/", "b"}, "a/b"}, | |
| JoinTest{[]string{"a/", ""}, "a"}, | |
| JoinTest{[]string{"", ""}, ""}, | |
| } | |
| // join takes a []string and passes it to Join. | |
| func join(elem []string, args ...string) string { | |
| args = elem | |
| return Join(args) | |
| } | |
| func TestJoin(t *testing.T) { | |
| for _, test := range jointests { | |
| if p := join(test.elem); p != test.path { | |
| t.Errorf("join(%q) = %q, want %q", test.elem, p, test.path) | |
| } | |
| } | |
| } | |
| type ExtTest struct { | |
| path, ext string | |
| } | |
| var exttests = []ExtTest{ | |
| ExtTest{"path.go", ".go"}, | |
| ExtTest{"path.pb.go", ".go"}, | |
| ExtTest{"a.dir/b", ""}, | |
| ExtTest{"a.dir/b.go", ".go"}, | |
| ExtTest{"a.dir/", ""}, | |
| } | |
| func TestExt(t *testing.T) { | |
| for _, test := range exttests { | |
| if x := 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{ | |
| &Node{"a", nil, 0}, | |
| &Node{"b", []*Node{}, 0}, | |
| &Node{"c", nil, 0}, | |
| &Node{ | |
| "d", | |
| []*Node{ | |
| &Node{"x", nil, 0}, | |
| &Node{"y", []*Node{}, 0}, | |
| &Node{ | |
| "z", | |
| []*Node{ | |
| &Node{"u", nil, 0}, | |
| &Node{"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, 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.Open(path, os.O_CREAT, 0660) | |
| if err != nil { | |
| t.Errorf("makeTree: %v", err) | |
| } | |
| 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) { | |
| walkTree(tree, tree.name, func(path string, n *Node) { | |
| if n.mark != 1 { | |
| 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. | |
| func mark(name string) { | |
| walkTree(tree, tree.name, func(path string, n *Node) { | |
| if n.name == name { | |
| n.mark++ | |
| } | |
| }) | |
| } | |
| type TestVisitor struct{} | |
| func (v *TestVisitor) VisitDir(path string, f *os.FileInfo) bool { | |
| mark(f.Name) | |
| return true | |
| } | |
| func (v *TestVisitor) VisitFile(path string, f *os.FileInfo) { | |
| mark(f.Name) | |
| } | |
| func TestWalk(t *testing.T) { | |
| makeTree(t) | |
| // 1) ignore error handling, expect none | |
| v := &TestVisitor{} | |
| Walk(tree.name, v, nil) | |
| checkMarks(t) | |
| // 2) handle errors, expect none | |
| errors := make(chan os.Error, 64) | |
| Walk(tree.name, v, errors) | |
| if err, ok := <-errors; ok { | |
| t.Errorf("no error expected, found: s", err) | |
| } | |
| checkMarks(t) | |
| if os.Getuid() != 0 { | |
| // introduce 2 errors: chmod top-level directories to 0 | |
| os.Chmod(Join(tree.name, tree.entries[1].name), 0) | |
| os.Chmod(Join(tree.name, tree.entries[3].name), 0) | |
| // 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-- | |
| // 3) handle errors, expect two | |
| errors = make(chan os.Error, 64) | |
| os.Chmod(Join(tree.name, tree.entries[1].name), 0) | |
| Walk(tree.name, v, errors) | |
| for i := 1; i <= 2; i++ { | |
| if _, ok := <-errors; !ok { | |
| t.Errorf("%d. error expected, none found", i) | |
| break | |
| } | |
| } | |
| if err, ok := <-errors; ok { | |
| t.Errorf("only two errors expected, found 3rd: %v", err) | |
| } | |
| // the inaccessible subtrees were marked manually | |
| checkMarks(t) | |
| } | |
| // cleanup | |
| os.Chmod(Join(tree.name, tree.entries[1].name), 0770) | |
| os.Chmod(Join(tree.name, tree.entries[3].name), 0770) | |
| if err := os.RemoveAll(tree.name); err != nil { | |
| t.Errorf("removeTree: %v", err) | |
| } | |
| } | |
| var basetests = []CleanTest{ | |
| // Already clean | |
| CleanTest{"", "."}, | |
| CleanTest{".", "."}, | |
| CleanTest{"/.", "."}, | |
| CleanTest{"/", "/"}, | |
| CleanTest{"////", "/"}, | |
| CleanTest{"x/", "x"}, | |
| CleanTest{"abc", "abc"}, | |
| CleanTest{"abc/def", "def"}, | |
| CleanTest{"a/b/.x", ".x"}, | |
| CleanTest{"a/b/c.", "c."}, | |
| CleanTest{"a/b/c.x", "c.x"}, | |
| } | |
| func TestBase(t *testing.T) { | |
| for _, test := range basetests { | |
| if s := Base(test.path); s != test.clean { | |
| t.Errorf("Base(%q) = %q, want %q", test.path, s, test.clean) | |
| } | |
| } | |
| } |