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.
22 type PathTest
struct {
26 var cleantests
= []PathTest
{
29 {"abc/def", "abc/def"},
34 {"../../abc", "../../abc"},
38 // Empty is current dir
41 // Remove trailing slash
43 {"abc/def/", "abc/def"},
50 // Remove doubled slash
51 {"abc//def//ghi", "abc/def/ghi"},
58 {"abc/./def", "abc/def"},
59 {"/./abc/def", "/abc/def"},
63 {"abc/def/ghi/../jkl", "abc/def/jkl"},
64 {"abc/def/../ghi/../jkl", "abc/jkl"},
65 {"abc/def/..", "abc"},
66 {"abc/def/../..", "."},
67 {"/abc/def/../..", "/"},
68 {"abc/def/../../..", ".."},
69 {"/abc/def/../../..", "/"},
70 {"abc/def/../../../ghi/jkl/../../../mno", "../../mno"},
74 {"abc/./../def", "def"},
75 {"abc//./../def", "def"},
76 {"abc/../../././../def", "../../def"},
79 var wincleantests
= []PathTest
{
83 {`c:abc\..\..\.\.\..\def`, `c:..\..\def`},
84 {`c:\abc\def\..\..`, `c:\`},
85 {`c:\..\abc`, `c:\abc`},
86 {`c:..\abc`, `c:..\abc`},
90 {`\\i\..\i\c$`, `\i\c$`},
91 {`\\i\..\I\c$`, `\I\c$`},
92 {`\\host\share\foo\..\bar`, `\\host\share\bar`},
93 {`//host/share/foo/../baz`, `\\host\share\baz`},
94 {`\\a\b\..\c`, `\\a\b\c`},
98 func TestClean(t
*testing
.T
) {
100 if runtime
.GOOS
== "windows" {
101 for i
:= range tests
{
102 tests
[i
].result
= filepath
.FromSlash(tests
[i
].result
)
104 tests
= append(tests
, wincleantests
...)
106 for _
, test
:= range tests
{
107 if s
:= filepath
.Clean(test
.path
); s
!= test
.result
{
108 t
.Errorf("Clean(%q) = %q, want %q", test
.path
, s
, test
.result
)
110 if s
:= filepath
.Clean(test
.result
); s
!= test
.result
{
111 t
.Errorf("Clean(%q) = %q, want %q", test
.result
, s
, test
.result
)
116 t
.Skip("skipping malloc count in short mode")
118 if runtime
.GOMAXPROCS(0) > 1 {
119 t
.Log("skipping AllocsPerRun checks; GOMAXPROCS>1")
123 t
.Log("Skipping AllocsPerRun for gccgo")
126 for _
, test
:= range tests
{
127 allocs
:= testing
.AllocsPerRun(100, func() { filepath
.Clean(test
.result
) })
129 t
.Errorf("Clean(%q): %v allocs, want zero", test
.result
, allocs
)
134 const sep
= filepath
.Separator
136 var slashtests
= []PathTest
{
139 {"/a/b", string([]byte{sep
, 'a', sep
, 'b'})},
140 {"a//b", string([]byte{'a', sep
, sep
, 'b'})},
143 func TestFromAndToSlash(t
*testing
.T
) {
144 for _
, test
:= range slashtests
{
145 if s
:= filepath
.FromSlash(test
.path
); s
!= test
.result
{
146 t
.Errorf("FromSlash(%q) = %q, want %q", test
.path
, s
, test
.result
)
148 if s
:= filepath
.ToSlash(test
.result
); s
!= test
.path
{
149 t
.Errorf("ToSlash(%q) = %q, want %q", test
.result
, s
, test
.path
)
154 type SplitListTest
struct {
159 const lsep
= filepath
.ListSeparator
161 var splitlisttests
= []SplitListTest
{
163 {string([]byte{'a', lsep
, 'b'}), []string{"a", "b"}},
164 {string([]byte{lsep
, 'a', lsep
, 'b'}), []string{"", "a", "b"}},
167 var winsplitlisttests
= []SplitListTest
{
169 {`"a"`, []string{`a`}},
172 {`";"`, []string{`;`}},
173 {`"a;b"`, []string{`a;b`}},
174 {`";";`, []string{`;`, ``}},
175 {`;";"`, []string{``, `;`}},
178 {`a";"b`, []string{`a;b`}},
179 {`a; ""b`, []string{`a`, ` b`}},
180 {`"a;b`, []string{`a;b`}},
181 {`""a;b`, []string{`a`, `b`}},
182 {`"""a;b`, []string{`a;b`}},
183 {`""""a;b`, []string{`a`, `b`}},
184 {`a";b`, []string{`a;b`}},
185 {`a;b";c`, []string{`a`, `b;c`}},
186 {`"a";b";c`, []string{`a`, `b;c`}},
189 func TestSplitList(t
*testing
.T
) {
190 tests
:= splitlisttests
191 if runtime
.GOOS
== "windows" {
192 tests
= append(tests
, winsplitlisttests
...)
194 for _
, test
:= range tests
{
195 if l
:= filepath
.SplitList(test
.list
); !reflect
.DeepEqual(l
, test
.result
) {
196 t
.Errorf("SplitList(%#q) = %#q, want %#q", test
.list
, l
, test
.result
)
201 type SplitTest
struct {
202 path
, dir
, file
string
205 var unixsplittests
= []SplitTest
{
207 {"a/b/", "a/b/", ""},
213 var winsplittests
= []SplitTest
{
216 {`c:/foo`, `c:/`, `foo`},
217 {`c:/foo/bar`, `c:/foo/`, `bar`},
218 {`//host/share`, `//host/share`, ``},
219 {`//host/share/`, `//host/share/`, ``},
220 {`//host/share/foo`, `//host/share/`, `foo`},
221 {`\\host\share`, `\\host\share`, ``},
222 {`\\host\share\`, `\\host\share\`, ``},
223 {`\\host\share\foo`, `\\host\share\`, `foo`},
226 func TestSplit(t
*testing
.T
) {
227 var splittests
[]SplitTest
228 splittests
= unixsplittests
229 if runtime
.GOOS
== "windows" {
230 splittests
= append(splittests
, winsplittests
...)
232 for _
, test
:= range splittests
{
233 if d
, f
:= filepath
.Split(test
.path
); d
!= test
.dir || f
!= test
.file
{
234 t
.Errorf("Split(%q) = %q, %q, want %q, %q", test
.path
, d
, f
, test
.dir
, test
.file
)
239 type JoinTest
struct {
244 var jointests
= []JoinTest
{
250 {[]string{"/"}, "/"},
251 {[]string{"a"}, "a"},
254 {[]string{"a", "b"}, "a/b"},
255 {[]string{"a", ""}, "a"},
256 {[]string{"", "b"}, "b"},
257 {[]string{"/", "a"}, "/a"},
258 {[]string{"/", "a/b"}, "/a/b"},
259 {[]string{"/", ""}, "/"},
260 {[]string{"//", "a"}, "/a"},
261 {[]string{"/a", "b"}, "/a/b"},
262 {[]string{"a/", "b"}, "a/b"},
263 {[]string{"a/", ""}, "a"},
264 {[]string{"", ""}, ""},
267 {[]string{"/", "a", "b"}, "/a/b"},
270 var winjointests
= []JoinTest
{
271 {[]string{`directory`, `file`}, `directory\file`},
272 {[]string{`C:\Windows\`, `System32`}, `C:\Windows\System32`},
273 {[]string{`C:\Windows\`, ``}, `C:\Windows`},
274 {[]string{`C:\`, `Windows`}, `C:\Windows`},
275 {[]string{`C:`, `a`}, `C:a`},
276 {[]string{`C:`, `a\b`}, `C:a\b`},
277 {[]string{`C:`, `a`, `b`}, `C:a\b`},
278 {[]string{`C:`, ``, `b`}, `C:b`},
279 {[]string{`C:`, ``, ``, `b`}, `C:b`},
280 {[]string{`C:`, ``}, `C:.`},
281 {[]string{`C:`, ``, ``}, `C:.`},
282 {[]string{`C:.`, `a`}, `C:a`},
283 {[]string{`C:a`, `b`}, `C:a\b`},
284 {[]string{`C:a`, `b`, `d`}, `C:a\b\d`},
285 {[]string{`\\host\share`, `foo`}, `\\host\share\foo`},
286 {[]string{`\\host\share\foo`}, `\\host\share\foo`},
287 {[]string{`//host/share`, `foo/bar`}, `\\host\share\foo\bar`},
288 {[]string{`\`}, `\`},
289 {[]string{`\`, ``}, `\`},
290 {[]string{`\`, `a`}, `\a`},
291 {[]string{`\\`, `a`}, `\a`},
292 {[]string{`\`, `a`, `b`}, `\a\b`},
293 {[]string{`\\`, `a`, `b`}, `\a\b`},
294 {[]string{`\`, `\\a\b`, `c`}, `\a\b\c`},
295 {[]string{`\\a`, `b`, `c`}, `\a\b\c`},
296 {[]string{`\\a\`, `b`, `c`}, `\a\b\c`},
299 func TestJoin(t
*testing
.T
) {
300 if runtime
.GOOS
== "windows" {
301 jointests
= append(jointests
, winjointests
...)
303 for _
, test
:= range jointests
{
304 expected
:= filepath
.FromSlash(test
.path
)
305 if p
:= filepath
.Join(test
.elem
...); p
!= expected
{
306 t
.Errorf("join(%q) = %q, want %q", test
.elem
, p
, expected
)
311 type ExtTest
struct {
315 var exttests
= []ExtTest
{
317 {"path.pb.go", ".go"},
319 {"a.dir/b.go", ".go"},
323 func TestExt(t
*testing
.T
) {
324 for _
, test
:= range exttests
{
325 if x
:= filepath
.Ext(test
.path
); x
!= test
.ext
{
326 t
.Errorf("Ext(%q) = %q, want %q", test
.path
, x
, test
.ext
)
333 entries
[]*Node
// nil if the entry is a file
363 func walkTree(n
*Node
, path
string, f
func(path
string, n
*Node
)) {
365 for _
, e
:= range n
.entries
{
366 walkTree(e
, filepath
.Join(path
, e
.name
), f
)
370 func makeTree(t
*testing
.T
) {
371 walkTree(tree
, tree
.name
, func(path
string, n
*Node
) {
372 if n
.entries
== nil {
373 fd
, err
:= os
.Create(path
)
375 t
.Errorf("makeTree: %v", err
)
385 func markTree(n
*Node
) { walkTree(n
, "", func(path
string, n
*Node
) { n
.mark
++ }) }
387 func checkMarks(t
*testing
.T
, report
bool) {
388 walkTree(tree
, tree
.name
, func(path
string, n
*Node
) {
389 if n
.mark
!= 1 && report
{
390 t
.Errorf("node %s mark = %d; expected 1", path
, n
.mark
)
396 // Assumes that each node name is unique. Good enough for a test.
397 // If clear is true, any incoming error is cleared before return. The errors
398 // are always accumulated, though.
399 func mark(d fs
.DirEntry
, err error
, errors
*[]error
, clear
bool) error
{
401 walkTree(tree
, tree
.name
, func(path
string, n
*Node
) {
407 *errors
= append(*errors
, err
)
416 // chdir changes the current working directory to the named directory,
417 // and then restore the original working directory at the end of the test.
418 func chdir(t
*testing
.T
, dir
string) {
419 olddir
, err
:= os
.Getwd()
421 t
.Fatalf("getwd %s: %v", dir
, err
)
423 if err
:= os
.Chdir(dir
); err
!= nil {
424 t
.Fatalf("chdir %s: %v", dir
, err
)
428 if err
:= os
.Chdir(olddir
); err
!= nil {
429 t
.Errorf("restore original working directory %s: %v", olddir
, err
)
435 func chtmpdir(t
*testing
.T
) (restore
func()) {
436 oldwd
, err
:= os
.Getwd()
438 t
.Fatalf("chtmpdir: %v", err
)
440 d
, err
:= os
.MkdirTemp("", "test")
442 t
.Fatalf("chtmpdir: %v", err
)
444 if err
:= os
.Chdir(d
); err
!= nil {
445 t
.Fatalf("chtmpdir: %v", err
)
448 if err
:= os
.Chdir(oldwd
); err
!= nil {
449 t
.Fatalf("chtmpdir: %v", err
)
455 // tempDirCanonical returns a temporary directory for the test to use, ensuring
456 // that the returned path does not contain symlinks.
457 func tempDirCanonical(t
*testing
.T
) string {
460 cdir
, err
:= filepath
.EvalSymlinks(dir
)
462 t
.Errorf("tempDirCanonical: %v", err
)
468 func TestWalk(t
*testing
.T
) {
469 walk
:= func(root
string, fn fs
.WalkDirFunc
) error
{
470 return filepath
.Walk(root
, func(path
string, info fs
.FileInfo
, err error
) error
{
471 return fn(path
, &statDirEntry
{info
}, err
)
477 type statDirEntry
struct {
481 func (d
*statDirEntry
) Name() string { return d
.info
.Name() }
482 func (d
*statDirEntry
) IsDir() bool { return d
.info
.IsDir() }
483 func (d
*statDirEntry
) Type() fs
.FileMode
{ return d
.info
.Mode().Type() }
484 func (d
*statDirEntry
) Info() (fs
.FileInfo
, error
) { return d
.info
, nil }
486 func TestWalkDir(t
*testing
.T
) {
487 testWalk(t
, filepath
.WalkDir
, 2)
490 func testWalk(t
*testing
.T
, walk
func(string, fs
.WalkDirFunc
) error
, errVisit
int) {
491 if runtime
.GOOS
== "ios" {
492 restore
:= chtmpdir(t
)
496 tmpDir
:= t
.TempDir()
498 origDir
, err
:= os
.Getwd()
500 t
.Fatal("finding working dir:", err
)
502 if err
= os
.Chdir(tmpDir
); err
!= nil {
503 t
.Fatal("entering temp dir:", err
)
505 defer os
.Chdir(origDir
)
508 errors
:= make([]error
, 0, 10)
510 markFn
:= func(path
string, d fs
.DirEntry
, err error
) error
{
511 return mark(d
, err
, &errors
, clear
)
514 err
= walk(tree
.name
, markFn
)
516 t
.Fatalf("no error expected, found: %s", err
)
518 if len(errors
) != 0 {
519 t
.Fatalf("unexpected errors: %s", errors
)
524 t
.Run("PermErr", func(t
*testing
.T
) {
525 // Test permission errors. Only possible if we're not root
526 // and only on some file systems (AFS, FAT). To avoid errors during
527 // all.bash on those file systems, skip during go test -short.
528 if runtime
.GOOS
== "windows" {
529 t
.Skip("skipping on Windows")
531 if os
.Getuid() == 0 {
532 t
.Skip("skipping as root")
535 t
.Skip("skipping in short mode")
538 // introduce 2 errors: chmod top-level directories to 0
539 os
.Chmod(filepath
.Join(tree
.name
, tree
.entries
[1].name
), 0)
540 os
.Chmod(filepath
.Join(tree
.name
, tree
.entries
[3].name
), 0)
542 // 3) capture errors, expect two.
543 // mark respective subtrees manually
544 markTree(tree
.entries
[1])
545 markTree(tree
.entries
[3])
546 // correct double-marking of directory itself
547 tree
.entries
[1].mark
-= errVisit
548 tree
.entries
[3].mark
-= errVisit
549 err
:= walk(tree
.name
, markFn
)
551 t
.Fatalf("expected no error return from Walk, got %s", err
)
553 if len(errors
) != 2 {
554 t
.Errorf("expected 2 errors, got %d: %s", len(errors
), errors
)
556 // the inaccessible subtrees were marked manually
560 // 4) capture errors, stop after first error.
561 // mark respective subtrees manually
562 markTree(tree
.entries
[1])
563 markTree(tree
.entries
[3])
564 // correct double-marking of directory itself
565 tree
.entries
[1].mark
-= errVisit
566 tree
.entries
[3].mark
-= errVisit
567 clear
= false // error will stop processing
568 err
= walk(tree
.name
, markFn
)
570 t
.Fatalf("expected error return from Walk")
572 if len(errors
) != 1 {
573 t
.Errorf("expected 1 error, got %d: %s", len(errors
), errors
)
575 // the inaccessible subtrees were marked manually
579 // restore permissions
580 os
.Chmod(filepath
.Join(tree
.name
, tree
.entries
[1].name
), 0770)
581 os
.Chmod(filepath
.Join(tree
.name
, tree
.entries
[3].name
), 0770)
585 func touch(t
*testing
.T
, name
string) {
586 f
, err
:= os
.Create(name
)
590 if err
:= f
.Close(); err
!= nil {
595 func TestWalkSkipDirOnFile(t
*testing
.T
) {
598 if err
:= os
.MkdirAll(filepath
.Join(td
, "dir"), 0755); err
!= nil {
601 touch(t
, filepath
.Join(td
, "dir/foo1"))
602 touch(t
, filepath
.Join(td
, "dir/foo2"))
605 walker
:= func(path
string) error
{
606 if strings
.HasSuffix(path
, "foo2") {
609 if strings
.HasSuffix(path
, "foo1") {
610 return filepath
.SkipDir
614 walkFn
:= func(path
string, _ fs
.FileInfo
, _ error
) error
{ return walker(path
) }
615 walkDirFn
:= func(path
string, _ fs
.DirEntry
, _ error
) error
{ return walker(path
) }
617 check
:= func(t
*testing
.T
, walk
func(root
string) error
, root
string) {
625 t
.Errorf("SkipDir on file foo1 did not block processing of foo2")
629 t
.Run("Walk", func(t
*testing
.T
) {
630 Walk
:= func(root
string) error
{ return filepath
.Walk(td
, walkFn
) }
632 check(t
, Walk
, filepath
.Join(td
, "dir"))
634 t
.Run("WalkDir", func(t
*testing
.T
) {
635 WalkDir
:= func(root
string) error
{ return filepath
.WalkDir(td
, walkDirFn
) }
636 check(t
, WalkDir
, td
)
637 check(t
, WalkDir
, filepath
.Join(td
, "dir"))
641 func TestWalkFileError(t
*testing
.T
) {
644 touch(t
, filepath
.Join(td
, "foo"))
645 touch(t
, filepath
.Join(td
, "bar"))
646 dir
:= filepath
.Join(td
, "dir")
647 if err
:= os
.MkdirAll(filepath
.Join(td
, "dir"), 0755); err
!= nil {
650 touch(t
, filepath
.Join(dir
, "baz"))
651 touch(t
, filepath
.Join(dir
, "stat-error"))
653 *filepath
.LstatP
= os
.Lstat
655 statErr
:= errors
.New("some stat error")
656 *filepath
.LstatP
= func(path
string) (fs
.FileInfo
, error
) {
657 if strings
.HasSuffix(path
, "stat-error") {
660 return os
.Lstat(path
)
662 got
:= map[string]error
{}
663 err
:= filepath
.Walk(td
, func(path
string, fi fs
.FileInfo
, err error
) error
{
664 rel
, _
:= filepath
.Rel(td
, path
)
665 got
[filepath
.ToSlash(rel
)] = err
669 t
.Errorf("Walk error: %v", err
)
671 want
:= map[string]error
{
677 "dir/stat-error": statErr
,
679 if !reflect
.DeepEqual(got
, want
) {
680 t
.Errorf("Walked %#v; want %#v", got
, want
)
684 var basetests
= []PathTest
{
698 var winbasetests
= []PathTest
{
704 {`\\host\share\`, `\`},
705 {`\\host\share\a`, `a`},
706 {`\\host\share\a\b`, `b`},
709 func TestBase(t
*testing
.T
) {
711 if runtime
.GOOS
== "windows" {
712 // make unix tests work on windows
713 for i
:= range tests
{
714 tests
[i
].result
= filepath
.Clean(tests
[i
].result
)
716 // add windows specific tests
717 tests
= append(tests
, winbasetests
...)
719 for _
, test
:= range tests
{
720 if s
:= filepath
.Base(test
.path
); s
!= test
.result
{
721 t
.Errorf("Base(%q) = %q, want %q", test
.path
, s
, test
.result
)
726 var dirtests
= []PathTest
{
741 var windirtests
= []PathTest
{
746 {`c:a\b\c`, `c:a\b`},
747 {`\\host\share`, `\\host\share`},
748 {`\\host\share\`, `\\host\share\`},
749 {`\\host\share\a`, `\\host\share\`},
750 {`\\host\share\a\b`, `\\host\share\a`},
753 func TestDir(t
*testing
.T
) {
755 if runtime
.GOOS
== "windows" {
756 // make unix tests work on windows
757 for i
:= range tests
{
758 tests
[i
].result
= filepath
.Clean(tests
[i
].result
)
760 // add windows specific tests
761 tests
= append(tests
, windirtests
...)
763 for _
, test
:= range tests
{
764 if s
:= filepath
.Dir(test
.path
); s
!= test
.result
{
765 t
.Errorf("Dir(%q) = %q, want %q", test
.path
, s
, test
.result
)
770 type IsAbsTest
struct {
775 var isabstests
= []IsAbsTest
{
778 {"/usr/bin/gcc", true},
786 var winisabstests
= []IsAbsTest
{
797 {`\\host\share`, true},
798 {`\\host\share\`, true},
799 {`\\host\share\foo`, true},
800 {`//host/share/foo/bar`, true},
803 func TestIsAbs(t
*testing
.T
) {
804 var tests
[]IsAbsTest
805 if runtime
.GOOS
== "windows" {
806 tests
= append(tests
, winisabstests
...)
807 // All non-windows tests should fail, because they have no volume letter.
808 for _
, test
:= range isabstests
{
809 tests
= append(tests
, IsAbsTest
{test
.path
, false})
811 // All non-windows test should work as intended if prefixed with volume letter.
812 for _
, test
:= range isabstests
{
813 tests
= append(tests
, IsAbsTest
{"c:" + test
.path
, test
.isAbs
})
815 // Test reserved names.
816 tests
= append(tests
, IsAbsTest
{os
.DevNull
, true})
817 tests
= append(tests
, IsAbsTest
{"NUL", true})
818 tests
= append(tests
, IsAbsTest
{"nul", true})
819 tests
= append(tests
, IsAbsTest
{"CON", true})
824 for _
, test
:= range tests
{
825 if r
:= filepath
.IsAbs(test
.path
); r
!= test
.isAbs
{
826 t
.Errorf("IsAbs(%q) = %v, want %v", test
.path
, r
, test
.isAbs
)
831 type EvalSymlinksTest
struct {
832 // If dest is empty, the path is created; otherwise the dest is symlinked to the path.
836 var EvalSymlinksTestDirs
= []EvalSymlinksTest
{
839 {"test/dir/link3", "../../"},
840 {"test/link1", "../test"},
841 {"test/link2", "dir"},
842 {"test/linkabs", "/"},
843 {"test/link4", "../test2"},
844 {"test2", "test/dir"},
848 {"src/pool/test", ""},
849 {"src/versions", ""},
850 {"src/versions/current", "../../version"},
851 {"src/versions/v1", ""},
852 {"src/versions/v1/modules", ""},
853 {"src/versions/v1/modules/test", "../../../pool/test"},
854 {"version", "src/versions/v1"},
857 var EvalSymlinksTests
= []EvalSymlinksTest
{
859 {"test/dir", "test/dir"},
860 {"test/dir/../..", "."},
861 {"test/link1", "test"},
862 {"test/link2", "test/dir"},
863 {"test/link1/dir", "test/dir"},
864 {"test/link2/..", "test"},
865 {"test/dir/link3", "."},
866 {"test/link2/link3/test", "test"},
867 {"test/linkabs", "/"},
868 {"test/link4/..", "test"},
869 {"src/versions/current/modules/test", "src/pool/test"},
872 // simpleJoin builds a file name from the directory and path.
873 // It does not use Join because we don't want ".." to be evaluated.
874 func simpleJoin(dir
, path
string) string {
875 return dir
+ string(filepath
.Separator
) + path
878 func testEvalSymlinks(t
*testing
.T
, path
, want
string) {
879 have
, err
:= filepath
.EvalSymlinks(path
)
881 t
.Errorf("EvalSymlinks(%q) error: %v", path
, err
)
884 if filepath
.Clean(have
) != filepath
.Clean(want
) {
885 t
.Errorf("EvalSymlinks(%q) returns %q, want %q", path
, have
, want
)
889 func testEvalSymlinksAfterChdir(t
*testing
.T
, wd
, path
, want
string) {
890 cwd
, err
:= os
.Getwd()
906 have
, err
:= filepath
.EvalSymlinks(path
)
908 t
.Errorf("EvalSymlinks(%q) in %q directory error: %v", path
, wd
, err
)
911 if filepath
.Clean(have
) != filepath
.Clean(want
) {
912 t
.Errorf("EvalSymlinks(%q) in %q directory returns %q, want %q", path
, wd
, have
, want
)
916 func TestEvalSymlinks(t
*testing
.T
) {
917 testenv
.MustHaveSymlink(t
)
919 tmpDir
:= t
.TempDir()
921 // /tmp may itself be a symlink! Avoid the confusion, although
922 // it means trusting the thing we're testing.
924 tmpDir
, err
= filepath
.EvalSymlinks(tmpDir
)
926 t
.Fatal("eval symlink for tmp dir:", err
)
929 // Create the symlink farm using relative paths.
930 for _
, d
:= range EvalSymlinksTestDirs
{
932 path
:= simpleJoin(tmpDir
, d
.path
)
934 err
= os
.Mkdir(path
, 0755)
936 err
= os
.Symlink(d
.dest
, path
)
943 // Evaluate the symlink farm.
944 for _
, test
:= range EvalSymlinksTests
{
945 path
:= simpleJoin(tmpDir
, test
.path
)
947 dest
:= simpleJoin(tmpDir
, test
.dest
)
948 if filepath
.IsAbs(test
.dest
) || os
.IsPathSeparator(test
.dest
[0]) {
951 testEvalSymlinks(t
, path
, dest
)
953 // test EvalSymlinks(".")
954 testEvalSymlinksAfterChdir(t
, path
, ".", ".")
956 // test EvalSymlinks("C:.") on Windows
957 if runtime
.GOOS
== "windows" {
958 volDot
:= filepath
.VolumeName(tmpDir
) + "."
959 testEvalSymlinksAfterChdir(t
, path
, volDot
, volDot
)
962 // test EvalSymlinks(".."+path)
963 dotdotPath
:= simpleJoin("..", test
.dest
)
964 if filepath
.IsAbs(test
.dest
) || os
.IsPathSeparator(test
.dest
[0]) {
965 dotdotPath
= test
.dest
967 testEvalSymlinksAfterChdir(t
,
968 simpleJoin(tmpDir
, "test"),
969 simpleJoin("..", test
.path
),
972 // test EvalSymlinks(p) where p is relative path
973 testEvalSymlinksAfterChdir(t
, tmpDir
, test
.path
, test
.dest
)
977 func TestEvalSymlinksIsNotExist(t
*testing
.T
) {
978 testenv
.MustHaveSymlink(t
)
982 _
, err
:= filepath
.EvalSymlinks("notexist")
983 if !os
.IsNotExist(err
) {
984 t
.Errorf("expected the file is not found, got %v\n", err
)
987 err
= os
.Symlink("notexist", "link")
991 defer os
.Remove("link")
993 _
, err
= filepath
.EvalSymlinks("link")
994 if !os
.IsNotExist(err
) {
995 t
.Errorf("expected the file is not found, got %v\n", err
)
999 func TestIssue13582(t
*testing
.T
) {
1000 testenv
.MustHaveSymlink(t
)
1002 tmpDir
:= t
.TempDir()
1004 dir
:= filepath
.Join(tmpDir
, "dir")
1005 err
:= os
.Mkdir(dir
, 0755)
1009 linkToDir
:= filepath
.Join(tmpDir
, "link_to_dir")
1010 err
= os
.Symlink(dir
, linkToDir
)
1014 file
:= filepath
.Join(linkToDir
, "file")
1015 err
= os
.WriteFile(file
, nil, 0644)
1019 link1
:= filepath
.Join(linkToDir
, "link1")
1020 err
= os
.Symlink(file
, link1
)
1024 link2
:= filepath
.Join(linkToDir
, "link2")
1025 err
= os
.Symlink(link1
, link2
)
1030 // /tmp may itself be a symlink!
1031 realTmpDir
, err
:= filepath
.EvalSymlinks(tmpDir
)
1035 realDir
:= filepath
.Join(realTmpDir
, "dir")
1036 realFile
:= filepath
.Join(realDir
, "file")
1042 {linkToDir
, realDir
},
1047 for i
, test
:= range tests
{
1048 have
, err
:= filepath
.EvalSymlinks(test
.path
)
1052 if have
!= test
.want
{
1053 t
.Errorf("test#%d: EvalSymlinks(%q) returns %q, want %q", i
, test
.path
, have
, test
.want
)
1058 // Test directories relative to temporary directory.
1059 // The tests are run in absTestDirs[0].
1060 var absTestDirs
= []string{
1066 // Test paths relative to temporary directory. $ expands to the directory.
1067 // The tests are run in absTestDirs[0].
1068 // We create absTestDirs first.
1069 var absTests
= []string{
1075 "../a/b/./c/../../.././a",
1076 "../a/b/./c/../../.././a/",
1080 "$/a/b/c/../../.././a",
1081 "$/a/b/c/../../.././a/",
1084 func TestAbs(t
*testing
.T
) {
1086 wd
, err
:= os
.Getwd()
1088 t
.Fatal("getwd failed: ", err
)
1090 err
= os
.Chdir(root
)
1092 t
.Fatal("chdir failed: ", err
)
1096 for _
, dir
:= range absTestDirs
{
1097 err
= os
.Mkdir(dir
, 0777)
1099 t
.Fatal("Mkdir failed: ", err
)
1103 if runtime
.GOOS
== "windows" {
1104 vol
:= filepath
.VolumeName(root
)
1106 for _
, path
:= range absTests
{
1107 if strings
.Contains(path
, "$") {
1111 extra
= append(extra
, path
)
1113 absTests
= append(absTests
, extra
...)
1116 err
= os
.Chdir(absTestDirs
[0])
1118 t
.Fatal("chdir failed: ", err
)
1121 for _
, path
:= range absTests
{
1122 path
= strings
.ReplaceAll(path
, "$", root
)
1123 info
, err
:= os
.Stat(path
)
1125 t
.Errorf("%s: %s", path
, err
)
1129 abspath
, err
:= filepath
.Abs(path
)
1131 t
.Errorf("Abs(%q) error: %v", path
, err
)
1134 absinfo
, err
:= os
.Stat(abspath
)
1135 if err
!= nil ||
!os
.SameFile(absinfo
, info
) {
1136 t
.Errorf("Abs(%q)=%q, not the same file", path
, abspath
)
1138 if !filepath
.IsAbs(abspath
) {
1139 t
.Errorf("Abs(%q)=%q, not an absolute path", path
, abspath
)
1141 if filepath
.IsAbs(abspath
) && abspath
!= filepath
.Clean(abspath
) {
1142 t
.Errorf("Abs(%q)=%q, isn't clean", path
, abspath
)
1147 // Empty path needs to be special-cased on Windows. See golang.org/issue/24441.
1148 // We test it separately from all other absTests because the empty string is not
1149 // a valid path, so it can't be used with os.Stat.
1150 func TestAbsEmptyString(t
*testing
.T
) {
1153 wd
, err
:= os
.Getwd()
1155 t
.Fatal("getwd failed: ", err
)
1157 err
= os
.Chdir(root
)
1159 t
.Fatal("chdir failed: ", err
)
1163 info
, err
:= os
.Stat(root
)
1165 t
.Fatalf("%s: %s", root
, err
)
1168 abspath
, err
:= filepath
.Abs("")
1170 t
.Fatalf(`Abs("") error: %v`, err
)
1172 absinfo
, err
:= os
.Stat(abspath
)
1173 if err
!= nil ||
!os
.SameFile(absinfo
, info
) {
1174 t
.Errorf(`Abs("")=%q, not the same file`, abspath
)
1176 if !filepath
.IsAbs(abspath
) {
1177 t
.Errorf(`Abs("")=%q, not an absolute path`, abspath
)
1179 if filepath
.IsAbs(abspath
) && abspath
!= filepath
.Clean(abspath
) {
1180 t
.Errorf(`Abs("")=%q, isn't clean`, abspath
)
1184 type RelTests
struct {
1185 root
, path
, want
string
1188 var reltests
= []RelTests
{
1189 {"a/b", "a/b", "."},
1190 {"a/b/.", "a/b", "."},
1191 {"a/b", "a/b/.", "."},
1192 {"./a/b", "a/b", "."},
1193 {"a/b", "./a/b", "."},
1194 {"ab/cd", "ab/cde", "../cde"},
1195 {"ab/cd", "ab/c", "../c"},
1196 {"a/b", "a/b/c/d", "c/d"},
1197 {"a/b", "a/b/../c", "../c"},
1198 {"a/b/../c", "a/b", "../b"},
1199 {"a/b/c", "a/c/d", "../../c/d"},
1200 {"a/b", "c/d", "../../c/d"},
1201 {"a/b/c/d", "a/b", "../.."},
1202 {"a/b/c/d", "a/b/", "../.."},
1203 {"a/b/c/d/", "a/b", "../.."},
1204 {"a/b/c/d/", "a/b/", "../.."},
1205 {"../../a/b", "../../a/b/c/d", "c/d"},
1206 {"/a/b", "/a/b", "."},
1207 {"/a/b/.", "/a/b", "."},
1208 {"/a/b", "/a/b/.", "."},
1209 {"/ab/cd", "/ab/cde", "../cde"},
1210 {"/ab/cd", "/ab/c", "../c"},
1211 {"/a/b", "/a/b/c/d", "c/d"},
1212 {"/a/b", "/a/b/../c", "../c"},
1213 {"/a/b/../c", "/a/b", "../b"},
1214 {"/a/b/c", "/a/c/d", "../../c/d"},
1215 {"/a/b", "/c/d", "../../c/d"},
1216 {"/a/b/c/d", "/a/b", "../.."},
1217 {"/a/b/c/d", "/a/b/", "../.."},
1218 {"/a/b/c/d/", "/a/b", "../.."},
1219 {"/a/b/c/d/", "/a/b/", "../.."},
1220 {"/../../a/b", "/../../a/b/c/d", "c/d"},
1221 {".", "a/b", "a/b"},
1224 // can't do purely lexically
1227 {"../..", "..", "err"},
1232 var winreltests
= []RelTests
{
1233 {`C:a\b\c`, `C:a/b/d`, `..\d`},
1234 {`C:\`, `D:\`, `err`},
1235 {`C:`, `D:`, `err`},
1236 {`C:\Projects`, `c:\projects\src`, `src`},
1237 {`C:\Projects`, `c:\projects`, `.`},
1238 {`C:\Projects\a\..`, `c:\projects`, `.`},
1239 {`\\host\share`, `\\host\share\file.txt`, `file.txt`},
1242 func TestRel(t
*testing
.T
) {
1243 tests
:= append([]RelTests
{}, reltests
...)
1244 if runtime
.GOOS
== "windows" {
1245 for i
:= range tests
{
1246 tests
[i
].want
= filepath
.FromSlash(tests
[i
].want
)
1248 tests
= append(tests
, winreltests
...)
1250 for _
, test
:= range tests
{
1251 got
, err
:= filepath
.Rel(test
.root
, test
.path
)
1252 if test
.want
== "err" {
1254 t
.Errorf("Rel(%q, %q)=%q, want error", test
.root
, test
.path
, got
)
1259 t
.Errorf("Rel(%q, %q): want %q, got error: %s", test
.root
, test
.path
, test
.want
, err
)
1261 if got
!= test
.want
{
1262 t
.Errorf("Rel(%q, %q)=%q, want %q", test
.root
, test
.path
, got
, test
.want
)
1267 type VolumeNameTest
struct {
1272 var volumenametests
= []VolumeNameTest
{
1273 {`c:/foo/bar`, `c:`},
1279 {`\\\host\share`, ``},
1280 {`\\\host\\share`, ``},
1285 {`\\host\share`, `\\host\share`},
1286 {`//host/share`, `//host/share`},
1287 {`\\host\share\`, `\\host\share`},
1288 {`//host/share/`, `//host/share`},
1289 {`\\host\share\foo`, `\\host\share`},
1290 {`//host/share/foo`, `//host/share`},
1291 {`\\host\share\\foo\\\bar\\\\baz`, `\\host\share`},
1292 {`//host/share//foo///bar////baz`, `//host/share`},
1293 {`\\host\share\foo\..\bar`, `\\host\share`},
1294 {`//host/share/foo/../bar`, `//host/share`},
1297 func TestVolumeName(t
*testing
.T
) {
1298 if runtime
.GOOS
!= "windows" {
1301 for _
, v
:= range volumenametests
{
1302 if vol
:= filepath
.VolumeName(v
.path
); vol
!= v
.vol
{
1303 t
.Errorf("VolumeName(%q)=%q, want %q", v
.path
, vol
, v
.vol
)
1308 func TestDriveLetterInEvalSymlinks(t
*testing
.T
) {
1309 if runtime
.GOOS
!= "windows" {
1314 t
.Errorf("Current directory path %q is too short", wd
)
1316 lp
:= strings
.ToLower(wd
)
1317 up
:= strings
.ToUpper(wd
)
1318 flp
, err
:= filepath
.EvalSymlinks(lp
)
1320 t
.Fatalf("EvalSymlinks(%q) failed: %q", lp
, err
)
1322 fup
, err
:= filepath
.EvalSymlinks(up
)
1324 t
.Fatalf("EvalSymlinks(%q) failed: %q", up
, err
)
1327 t
.Errorf("Results of EvalSymlinks do not match: %q and %q", flp
, fup
)
1331 func TestBug3486(t
*testing
.T
) { // https://golang.org/issue/3486
1332 t
.Skip("skipping test because gccgo sources are arranged differently.")
1333 if runtime
.GOOS
== "ios" {
1334 t
.Skipf("skipping on %s/%s", runtime
.GOOS
, runtime
.GOARCH
)
1336 root
, err
:= filepath
.EvalSymlinks(runtime
.GOROOT() + "/test")
1340 bugs
:= filepath
.Join(root
, "fixedbugs")
1341 ken
:= filepath
.Join(root
, "ken")
1344 err
= filepath
.Walk(root
, func(pth
string, info fs
.FileInfo
, err error
) error
{
1352 return filepath
.SkipDir
1355 t
.Fatal("filepath.Walk out of order - ken before fixedbugs")
1365 t
.Fatalf("%q not seen", ken
)
1369 func testWalkSymlink(t
*testing
.T
, mklink
func(target
, link
string) error
) {
1370 tmpdir
:= t
.TempDir()
1372 wd
, err
:= os
.Getwd()
1378 err
= os
.Chdir(tmpdir
)
1383 err
= mklink(tmpdir
, "link")
1388 var visited
[]string
1389 err
= filepath
.Walk(tmpdir
, func(path
string, info fs
.FileInfo
, err error
) error
{
1393 rel
, err
:= filepath
.Rel(tmpdir
, path
)
1397 visited
= append(visited
, rel
)
1403 sort
.Strings(visited
)
1404 want
:= []string{".", "link"}
1405 if fmt
.Sprintf("%q", visited
) != fmt
.Sprintf("%q", want
) {
1406 t
.Errorf("unexpected paths visited %q, want %q", visited
, want
)
1410 func TestWalkSymlink(t
*testing
.T
) {
1411 testenv
.MustHaveSymlink(t
)
1412 testWalkSymlink(t
, os
.Symlink
)
1415 func TestIssue29372(t
*testing
.T
) {
1416 tmpDir
:= t
.TempDir()
1418 path
:= filepath
.Join(tmpDir
, "file.txt")
1419 err
:= os
.WriteFile(path
, nil, 0644)
1424 pathSeparator
:= string(filepath
.Separator
)
1426 path
+ strings
.Repeat(pathSeparator
, 1),
1427 path
+ strings
.Repeat(pathSeparator
, 2),
1428 path
+ strings
.Repeat(pathSeparator
, 1) + ".",
1429 path
+ strings
.Repeat(pathSeparator
, 2) + ".",
1430 path
+ strings
.Repeat(pathSeparator
, 1) + "..",
1431 path
+ strings
.Repeat(pathSeparator
, 2) + "..",
1434 for i
, test
:= range tests
{
1435 _
, err
= filepath
.EvalSymlinks(test
)
1436 if err
!= syscall
.ENOTDIR
{
1437 t
.Fatalf("test#%d: want %q, got %q", i
, syscall
.ENOTDIR
, err
)
1442 // Issue 30520 part 1.
1443 func TestEvalSymlinksAboveRoot(t
*testing
.T
) {
1444 testenv
.MustHaveSymlink(t
)
1448 tmpDir
:= t
.TempDir()
1450 evalTmpDir
, err
:= filepath
.EvalSymlinks(tmpDir
)
1455 if err
:= os
.Mkdir(filepath
.Join(evalTmpDir
, "a"), 0777); err
!= nil {
1458 if err
:= os
.Symlink(filepath
.Join(evalTmpDir
, "a"), filepath
.Join(evalTmpDir
, "b")); err
!= nil {
1461 if err
:= os
.WriteFile(filepath
.Join(evalTmpDir
, "a", "file"), nil, 0666); err
!= nil {
1465 // Count the number of ".." elements to get to the root directory.
1466 vol
:= filepath
.VolumeName(evalTmpDir
)
1467 c
:= strings
.Count(evalTmpDir
[len(vol
):], string(os
.PathSeparator
))
1469 for i
:= 0; i
< c
+2; i
++ {
1470 dd
= append(dd
, "..")
1473 wantSuffix
:= strings
.Join([]string{"a", "file"}, string(os
.PathSeparator
))
1475 // Try different numbers of "..".
1476 for _
, i
:= range []int{c
, c
+ 1, c
+ 2} {
1477 check
:= strings
.Join([]string{evalTmpDir
, strings
.Join(dd
[:i
], string(os
.PathSeparator
)), evalTmpDir
[len(vol
)+1:], "b", "file"}, string(os
.PathSeparator
))
1478 resolved
, err
:= filepath
.EvalSymlinks(check
)
1480 case runtime
.GOOS
== "darwin" && errors
.Is(err
, fs
.ErrNotExist
):
1481 // On darwin, the temp dir is sometimes cleaned up mid-test (issue 37910).
1482 testenv
.SkipFlaky(t
, 37910)
1484 t
.Errorf("EvalSymlinks(%q) failed: %v", check
, err
)
1485 case !strings
.HasSuffix(resolved
, wantSuffix
):
1486 t
.Errorf("EvalSymlinks(%q) = %q does not end with %q", check
, resolved
, wantSuffix
)
1488 t
.Logf("EvalSymlinks(%q) = %q", check
, resolved
)
1493 // Issue 30520 part 2.
1494 func TestEvalSymlinksAboveRootChdir(t
*testing
.T
) {
1495 testenv
.MustHaveSymlink(t
)
1497 tmpDir
, err
:= os
.MkdirTemp("", "TestEvalSymlinksAboveRootChdir")
1501 defer os
.RemoveAll(tmpDir
)
1504 subdir
:= filepath
.Join("a", "b")
1505 if err
:= os
.MkdirAll(subdir
, 0777); err
!= nil {
1508 if err
:= os
.Symlink(subdir
, "c"); err
!= nil {
1511 if err
:= os
.WriteFile(filepath
.Join(subdir
, "file"), nil, 0666); err
!= nil {
1515 subdir
= filepath
.Join("d", "e", "f")
1516 if err
:= os
.MkdirAll(subdir
, 0777); err
!= nil {
1519 if err
:= os
.Chdir(subdir
); err
!= nil {
1523 check
:= filepath
.Join("..", "..", "..", "c", "file")
1524 wantSuffix
:= filepath
.Join("a", "b", "file")
1525 if resolved
, err
:= filepath
.EvalSymlinks(check
); err
!= nil {
1526 t
.Errorf("EvalSymlinks(%q) failed: %v", check
, err
)
1527 } else if !strings
.HasSuffix(resolved
, wantSuffix
) {
1528 t
.Errorf("EvalSymlinks(%q) = %q does not end with %q", check
, resolved
, wantSuffix
)
1530 t
.Logf("EvalSymlinks(%q) = %q", check
, resolved
)