1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
17 type PathTest struct {
21 var cleantests = []PathTest{
25 {"abc/def", "abc/def"},
30 {"../../abc", "../../abc"},
34 // Remove trailing slash
36 {"abc/def/", "abc/def"},
43 // Remove doubled slash
44 {"abc//def//ghi", "abc/def/ghi"},
51 {"abc/./def", "abc/def"},
52 {"/./abc/def", "/abc/def"},
56 {"abc/def/ghi/../jkl", "abc/def/jkl"},
57 {"abc/def/../ghi/../jkl", "abc/jkl"},
58 {"abc/def/..", "abc"},
59 {"abc/def/../..", "."},
60 {"/abc/def/../..", "/"},
61 {"abc/def/../../..", ".."},
62 {"/abc/def/../../..", "/"},
63 {"abc/def/../../../ghi/jkl/../../../mno", "../../mno"},
66 {"abc/./../def", "def"},
67 {"abc//./../def", "def"},
68 {"abc/../../././../def", "../../def"},
71 var wincleantests = []PathTest{
75 {`c:abc\..\..\.\.\..\def`, `c:..\..\def`},
76 {`c:\abc\def\..\..`, `c:\`},
77 {`c:\..\abc`, `c:\abc`},
78 {`c:..\abc`, `c:..\abc`},
82 {`\\i\..\i\c$`, `\i\c$`},
83 {`\\i\..\I\c$`, `\I\c$`},
84 {`\\host\share\foo\..\bar`, `\\host\share\bar`},
85 {`//host/share/foo/../baz`, `\\host\share\baz`},
86 {`\\a\b\..\c`, `\\a\b\c`},
90 func TestClean(t *testing.T) {
92 if runtime.GOOS == "windows" {
93 for i := range tests {
94 tests[i].result = filepath.FromSlash(tests[i].result)
96 tests = append(tests, wincleantests...)
98 for _, test := range tests {
99 if s := filepath.Clean(test.path); s != test.result {
100 t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result)
105 const sep = filepath.Separator
107 var slashtests = []PathTest{
110 {"/a/b", string([]byte{sep, 'a', sep, 'b'})},
111 {"a//b", string([]byte{'a', sep, sep, 'b'})},
114 func TestFromAndToSlash(t *testing.T) {
115 for _, test := range slashtests {
116 if s := filepath.FromSlash(test.path); s != test.result {
117 t.Errorf("FromSlash(%q) = %q, want %q", test.path, s, test.result)
119 if s := filepath.ToSlash(test.result); s != test.path {
120 t.Errorf("ToSlash(%q) = %q, want %q", test.result, s, test.path)
125 type SplitListTest struct {
130 const lsep = filepath.ListSeparator
132 var splitlisttests = []SplitListTest{
134 {string([]byte{'a', lsep, 'b'}), []string{"a", "b"}},
135 {string([]byte{lsep, 'a', lsep, 'b'}), []string{"", "a", "b"}},
138 func TestSplitList(t *testing.T) {
139 for _, test := range splitlisttests {
140 if l := filepath.SplitList(test.list); !reflect.DeepEqual(l, test.result) {
141 t.Errorf("SplitList(%q) = %s, want %s", test.list, l, test.result)
146 type SplitTest struct {
147 path, dir, file string
150 var unixsplittests = []SplitTest{
152 {"a/b/", "a/b/", ""},
158 var winsplittests = []SplitTest{
161 {`c:/foo`, `c:/`, `foo`},
162 {`c:/foo/bar`, `c:/foo/`, `bar`},
163 {`//host/share`, `//host/share`, ``},
164 {`//host/share/`, `//host/share/`, ``},
165 {`//host/share/foo`, `//host/share/`, `foo`},
166 {`\\host\share`, `\\host\share`, ``},
167 {`\\host\share\`, `\\host\share\`, ``},
168 {`\\host\share\foo`, `\\host\share\`, `foo`},
171 func TestSplit(t *testing.T) {
172 var splittests []SplitTest
173 splittests = unixsplittests
174 if runtime.GOOS == "windows" {
175 splittests = append(splittests, winsplittests...)
177 for _, test := range splittests {
178 if d, f := filepath.Split(test.path); d != test.dir || f != test.file {
179 t.Errorf("Split(%q) = %q, %q, want %q, %q", test.path, d, f, test.dir, test.file)
184 type JoinTest struct {
189 var jointests = []JoinTest{
195 {[]string{"a"}, "a"},
198 {[]string{"a", "b"}, "a/b"},
199 {[]string{"a", ""}, "a"},
200 {[]string{"", "b"}, "b"},
201 {[]string{"/", "a"}, "/a"},
202 {[]string{"/", ""}, "/"},
203 {[]string{"a/", "b"}, "a/b"},
204 {[]string{"a/", ""}, "a"},
205 {[]string{"", ""}, ""},
208 var winjointests = []JoinTest{
209 {[]string{`directory`, `file`}, `directory\file`},
210 {[]string{`C:\Windows\`, `System32`}, `C:\Windows\System32`},
211 {[]string{`C:\Windows\`, ``}, `C:\Windows`},
212 {[]string{`C:\`, `Windows`}, `C:\Windows`},
213 {[]string{`C:`, `Windows`}, `C:\Windows`},
214 {[]string{`\\host\share`, `foo`}, `\\host\share\foo`},
215 {[]string{`//host/share`, `foo/bar`}, `\\host\share\foo\bar`},
218 // join takes a []string and passes it to Join.
219 func join(elem []string, args ...string) string {
221 return filepath.Join(args...)
224 func TestJoin(t *testing.T) {
225 if runtime.GOOS == "windows" {
226 jointests = append(jointests, winjointests...)
228 for _, test := range jointests {
229 if p := join(test.elem); p != filepath.FromSlash(test.path) {
230 t.Errorf("join(%q) = %q, want %q", test.elem, p, test.path)
235 type ExtTest struct {
239 var exttests = []ExtTest{
241 {"path.pb.go", ".go"},
243 {"a.dir/b.go", ".go"},
247 func TestExt(t *testing.T) {
248 for _, test := range exttests {
249 if x := filepath.Ext(test.path); x != test.ext {
250 t.Errorf("Ext(%q) = %q, want %q", test.path, x, test.ext)
257 entries []*Node // nil if the entry is a file
287 func walkTree(n *Node, path string, f func(path string, n *Node)) {
289 for _, e := range n.entries {
290 walkTree(e, filepath.Join(path, e.name), f)
294 func makeTree(t *testing.T) {
295 walkTree(tree, tree.name, func(path string, n *Node) {
296 if n.entries == nil {
297 fd, err := os.Create(path)
299 t.Errorf("makeTree: %v", err)
309 func markTree(n *Node) { walkTree(n, "", func(path string, n *Node) { n.mark++ }) }
311 func checkMarks(t *testing.T, report bool) {
312 walkTree(tree, tree.name, func(path string, n *Node) {
313 if n.mark != 1 && report {
314 t.Errorf("node %s mark = %d; expected 1", path, n.mark)
320 // Assumes that each node name is unique. Good enough for a test.
321 // If clear is true, any incoming error is cleared before return. The errors
322 // are always accumulated, though.
323 func mark(path string, info os.FileInfo, err error, errors *[]error, clear bool) error {
325 *errors = append(*errors, err)
332 walkTree(tree, tree.name, func(path string, n *Node) {
340 func TestWalk(t *testing.T) {
342 errors := make([]error, 0, 10)
344 markFn := func(path string, info os.FileInfo, err error) error {
345 return mark(path, info, err, &errors, clear)
348 err := filepath.Walk(tree.name, markFn)
350 t.Fatalf("no error expected, found: %s", err)
352 if len(errors) != 0 {
353 t.Fatalf("unexpected errors: %s", errors)
358 // Test permission errors. Only possible if we're not root
359 // and only on some file systems (AFS, FAT). To avoid errors during
360 // all.bash on those file systems, skip during go test -short.
361 if os.Getuid() > 0 && !testing.Short() {
362 // introduce 2 errors: chmod top-level directories to 0
363 os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0)
364 os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0)
366 // 3) capture errors, expect two.
367 // mark respective subtrees manually
368 markTree(tree.entries[1])
369 markTree(tree.entries[3])
370 // correct double-marking of directory itself
371 tree.entries[1].mark--
372 tree.entries[3].mark--
373 err := filepath.Walk(tree.name, markFn)
375 t.Fatalf("expected no error return from Walk, got %s", err)
377 if len(errors) != 2 {
378 t.Errorf("expected 2 errors, got %d: %s", len(errors), errors)
380 // the inaccessible subtrees were marked manually
384 // 4) capture errors, stop after first error.
385 // mark respective subtrees manually
386 markTree(tree.entries[1])
387 markTree(tree.entries[3])
388 // correct double-marking of directory itself
389 tree.entries[1].mark--
390 tree.entries[3].mark--
391 clear = false // error will stop processing
392 err = filepath.Walk(tree.name, markFn)
394 t.Fatalf("expected error return from Walk")
396 if len(errors) != 1 {
397 t.Errorf("expected 1 error, got %d: %s", len(errors), errors)
399 // the inaccessible subtrees were marked manually
403 // restore permissions
404 os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770)
405 os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770)
409 if err := os.RemoveAll(tree.name); err != nil {
410 t.Errorf("removeTree: %v", err)
414 var basetests = []PathTest{
428 var winbasetests = []PathTest{
434 {`\\host\share\`, `\`},
435 {`\\host\share\a`, `a`},
436 {`\\host\share\a\b`, `b`},
439 func TestBase(t *testing.T) {
441 if runtime.GOOS == "windows" {
442 // make unix tests work on windows
443 for i := range tests {
444 tests[i].result = filepath.Clean(tests[i].result)
446 // add windows specific tests
447 tests = append(tests, winbasetests...)
449 for _, test := range tests {
450 if s := filepath.Base(test.path); s != test.result {
451 t.Errorf("Base(%q) = %q, want %q", test.path, s, test.result)
456 var dirtests = []PathTest{
471 var windirtests = []PathTest{
476 {`c:a\b\c`, `c:a\b`},
477 {`\\host\share\`, `\\host\share\`},
478 {`\\host\share\a`, `\\host\share\`},
479 {`\\host\share\a\b`, `\\host\share\a`},
482 func TestDir(t *testing.T) {
484 if runtime.GOOS == "windows" {
485 // make unix tests work on windows
486 for i := range tests {
487 tests[i].result = filepath.Clean(tests[i].result)
489 // add windows specific tests
490 tests = append(tests, windirtests...)
492 for _, test := range tests {
493 if s := filepath.Dir(test.path); s != test.result {
494 t.Errorf("Dir(%q) = %q, want %q", test.path, s, test.result)
499 type IsAbsTest struct {
504 var isabstests = []IsAbsTest{
507 {"/usr/bin/gcc", true},
515 var winisabstests = []IsAbsTest{
524 {`\\host\share\foo`, true},
525 {`//host/share/foo/bar`, true},
528 func TestIsAbs(t *testing.T) {
529 var tests []IsAbsTest
530 if runtime.GOOS == "windows" {
531 tests = append(tests, winisabstests...)
532 // All non-windows tests should fail, because they have no volume letter.
533 for _, test := range isabstests {
534 tests = append(tests, IsAbsTest{test.path, false})
536 // All non-windows test should work as intended if prefixed with volume letter.
537 for _, test := range isabstests {
538 tests = append(tests, IsAbsTest{"c:" + test.path, test.isAbs})
544 for _, test := range tests {
545 if r := filepath.IsAbs(test.path); r != test.isAbs {
546 t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs)
551 type EvalSymlinksTest struct {
552 // If dest is empty, the path is created; otherwise the dest is symlinked to the path.
556 var EvalSymlinksTestDirs = []EvalSymlinksTest{
559 {"test/dir/link3", "../../"},
560 {"test/link1", "../test"},
561 {"test/link2", "dir"},
562 {"test/linkabs", "/"},
565 var EvalSymlinksTests = []EvalSymlinksTest{
567 {"test/dir", "test/dir"},
568 {"test/dir/../..", "."},
569 {"test/link1", "test"},
570 {"test/link2", "test/dir"},
571 {"test/link1/dir", "test/dir"},
572 {"test/link2/..", "test"},
573 {"test/dir/link3", "."},
574 {"test/link2/link3/test", "test"},
575 {"test/linkabs", "/"},
578 var EvalSymlinksAbsWindowsTests = []EvalSymlinksTest{
582 // simpleJoin builds a file name from the directory and path.
583 // It does not use Join because we don't want ".." to be evaluated.
584 func simpleJoin(dir, path string) string {
585 return dir + string(filepath.Separator) + path
588 func TestEvalSymlinks(t *testing.T) {
589 tmpDir, err := ioutil.TempDir("", "evalsymlink")
591 t.Fatal("creating temp dir:", err)
593 defer os.RemoveAll(tmpDir)
595 // /tmp may itself be a symlink! Avoid the confusion, although
596 // it means trusting the thing we're testing.
597 tmpDir, err = filepath.EvalSymlinks(tmpDir)
599 t.Fatal("eval symlink for tmp dir:", err)
602 // Create the symlink farm using relative paths.
603 for _, d := range EvalSymlinksTestDirs {
605 path := simpleJoin(tmpDir, d.path)
607 err = os.Mkdir(path, 0755)
609 if runtime.GOOS != "windows" {
610 err = os.Symlink(d.dest, path)
618 var tests []EvalSymlinksTest
619 if runtime.GOOS == "windows" {
620 for _, d := range EvalSymlinksTests {
621 if d.path == d.dest {
622 // will test only real files and directories
623 tests = append(tests, d)
624 // test "canonical" names
625 d2 := EvalSymlinksTest{
626 path: strings.ToUpper(d.path),
629 tests = append(tests, d2)
633 tests = EvalSymlinksTests
636 // Evaluate the symlink farm.
637 for _, d := range tests {
638 path := simpleJoin(tmpDir, d.path)
639 dest := simpleJoin(tmpDir, d.dest)
640 if filepath.IsAbs(d.dest) {
643 if p, err := filepath.EvalSymlinks(path); err != nil {
644 t.Errorf("EvalSymlinks(%q) error: %v", d.path, err)
645 } else if filepath.Clean(p) != filepath.Clean(dest) {
646 t.Errorf("Clean(%q)=%q, want %q", path, p, dest)
651 // Test directories relative to temporary directory.
652 // The tests are run in absTestDirs[0].
653 var absTestDirs = []string{
659 // Test paths relative to temporary directory. $ expands to the directory.
660 // The tests are run in absTestDirs[0].
661 // We create absTestDirs first.
662 var absTests = []string{
667 "../a/b/./c/../../.././a",
671 "$/a/b/c/../../.././a",
674 func TestAbs(t *testing.T) {
675 oldwd, err := os.Getwd()
677 t.Fatal("Getwd failed: ", err)
679 defer os.Chdir(oldwd)
681 root, err := ioutil.TempDir("", "TestAbs")
683 t.Fatal("TempDir failed: ", err)
685 defer os.RemoveAll(root)
689 t.Fatal("chdir failed: ", err)
692 for _, dir := range absTestDirs {
693 err = os.Mkdir(dir, 0777)
695 t.Fatal("Mkdir failed: ", err)
699 err = os.Chdir(absTestDirs[0])
701 t.Fatal("chdir failed: ", err)
704 for _, path := range absTests {
705 path = strings.Replace(path, "$", root, -1)
706 info, err := os.Stat(path)
708 t.Errorf("%s: %s", path, err)
712 abspath, err := filepath.Abs(path)
714 t.Errorf("Abs(%q) error: %v", path, err)
717 absinfo, err := os.Stat(abspath)
718 if err != nil || !os.SameFile(absinfo, info) {
719 t.Errorf("Abs(%q)=%q, not the same file", path, abspath)
721 if !filepath.IsAbs(abspath) {
722 t.Errorf("Abs(%q)=%q, not an absolute path", path, abspath)
724 if filepath.IsAbs(path) && abspath != filepath.Clean(path) {
725 t.Errorf("Abs(%q)=%q, isn't clean", path, abspath)
730 type RelTests struct {
731 root, path, want string
734 var reltests = []RelTests{
736 {"a/b/.", "a/b", "."},
737 {"a/b", "a/b/.", "."},
738 {"./a/b", "a/b", "."},
739 {"a/b", "./a/b", "."},
740 {"ab/cd", "ab/cde", "../cde"},
741 {"ab/cd", "ab/c", "../c"},
742 {"a/b", "a/b/c/d", "c/d"},
743 {"a/b", "a/b/../c", "../c"},
744 {"a/b/../c", "a/b", "../b"},
745 {"a/b/c", "a/c/d", "../../c/d"},
746 {"a/b", "c/d", "../../c/d"},
747 {"a/b/c/d", "a/b", "../.."},
748 {"a/b/c/d", "a/b/", "../.."},
749 {"a/b/c/d/", "a/b", "../.."},
750 {"a/b/c/d/", "a/b/", "../.."},
751 {"../../a/b", "../../a/b/c/d", "c/d"},
752 {"/a/b", "/a/b", "."},
753 {"/a/b/.", "/a/b", "."},
754 {"/a/b", "/a/b/.", "."},
755 {"/ab/cd", "/ab/cde", "../cde"},
756 {"/ab/cd", "/ab/c", "../c"},
757 {"/a/b", "/a/b/c/d", "c/d"},
758 {"/a/b", "/a/b/../c", "../c"},
759 {"/a/b/../c", "/a/b", "../b"},
760 {"/a/b/c", "/a/c/d", "../../c/d"},
761 {"/a/b", "/c/d", "../../c/d"},
762 {"/a/b/c/d", "/a/b", "../.."},
763 {"/a/b/c/d", "/a/b/", "../.."},
764 {"/a/b/c/d/", "/a/b", "../.."},
765 {"/a/b/c/d/", "/a/b/", "../.."},
766 {"/../../a/b", "/../../a/b/c/d", "c/d"},
770 // can't do purely lexically
773 {"../..", "..", "err"},
778 var winreltests = []RelTests{
779 {`C:a\b\c`, `C:a/b/d`, `..\d`},
780 {`C:\`, `D:\`, `err`},
784 func TestRel(t *testing.T) {
785 tests := append([]RelTests{}, reltests...)
786 if runtime.GOOS == "windows" {
787 for i := range tests {
788 tests[i].want = filepath.FromSlash(tests[i].want)
790 tests = append(tests, winreltests...)
792 for _, test := range tests {
793 got, err := filepath.Rel(test.root, test.path)
794 if test.want == "err" {
796 t.Errorf("Rel(%q, %q)=%q, want error", test.root, test.path, got)
801 t.Errorf("Rel(%q, %q): want %q, got error: %s", test.root, test.path, test.want, err)
803 if got != test.want {
804 t.Errorf("Rel(%q, %q)=%q, want %q", test.root, test.path, got, test.want)
809 type VolumeNameTest struct {
814 var volumenametests = []VolumeNameTest{
815 {`c:/foo/bar`, `c:`},
820 {`\\\host\share`, ``},
821 {`\\\host\\share`, ``},
826 {`\\host\share`, `\\host\share`},
827 {`//host/share`, `//host/share`},
828 {`\\host\share\`, `\\host\share`},
829 {`//host/share/`, `//host/share`},
830 {`\\host\share\foo`, `\\host\share`},
831 {`//host/share/foo`, `//host/share`},
832 {`\\host\share\\foo\\\bar\\\\baz`, `\\host\share`},
833 {`//host/share//foo///bar////baz`, `//host/share`},
834 {`\\host\share\foo\..\bar`, `\\host\share`},
835 {`//host/share/foo/../bar`, `//host/share`},
838 func TestVolumeName(t *testing.T) {
839 if runtime.GOOS != "windows" {
842 for _, v := range volumenametests {
843 if vol := filepath.VolumeName(v.path); vol != v.vol {
844 t.Errorf("VolumeName(%q)=%q, want %q", v.path, vol, v.vol)