1
2
3
4
5 package filepath_test
6
7 import (
8 "errors"
9 "fmt"
10 "internal/testenv"
11 "io/fs"
12 "os"
13 "path/filepath"
14 "reflect"
15 "runtime"
16 "sort"
17 "strings"
18 "syscall"
19 "testing"
20 )
21
22 type PathTest struct {
23 path, result string
24 }
25
26 var cleantests = []PathTest{
27
28 {"abc", "abc"},
29 {"abc/def", "abc/def"},
30 {"a/b/c", "a/b/c"},
31 {".", "."},
32 {"..", ".."},
33 {"../..", "../.."},
34 {"../../abc", "../../abc"},
35 {"/abc", "/abc"},
36 {"/", "/"},
37
38
39 {"", "."},
40
41
42 {"abc/", "abc"},
43 {"abc/def/", "abc/def"},
44 {"a/b/c/", "a/b/c"},
45 {"./", "."},
46 {"../", ".."},
47 {"../../", "../.."},
48 {"/abc/", "/abc"},
49
50
51 {"abc//def//ghi", "abc/def/ghi"},
52 {"//abc", "/abc"},
53 {"///abc", "/abc"},
54 {"//abc//", "/abc"},
55 {"abc//", "abc"},
56
57
58 {"abc/./def", "abc/def"},
59 {"/./abc/def", "/abc/def"},
60 {"abc/.", "abc"},
61
62
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"},
71 {"/../abc", "/abc"},
72
73
74 {"abc/./../def", "def"},
75 {"abc//./../def", "def"},
76 {"abc/../../././../def", "../../def"},
77 }
78
79 var wincleantests = []PathTest{
80 {`c:`, `c:.`},
81 {`c:\`, `c:\`},
82 {`c:\abc`, `c:\abc`},
83 {`c:abc\..\..\.\.\..\def`, `c:..\..\def`},
84 {`c:\abc\def\..\..`, `c:\`},
85 {`c:\..\abc`, `c:\abc`},
86 {`c:..\abc`, `c:..\abc`},
87 {`\`, `\`},
88 {`/`, `\`},
89 {`\\i\..\c$`, `\c$`},
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`},
95 {`\\a\b`, `\\a\b`},
96 {`.\c:`, `.\c:`},
97 {`.\c:\foo`, `.\c:\foo`},
98 {`.\c:foo`, `.\c:foo`},
99 }
100
101 func TestClean(t *testing.T) {
102 tests := cleantests
103 if runtime.GOOS == "windows" {
104 for i := range tests {
105 tests[i].result = filepath.FromSlash(tests[i].result)
106 }
107 tests = append(tests, wincleantests...)
108 }
109 for _, test := range tests {
110 if s := filepath.Clean(test.path); s != test.result {
111 t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result)
112 }
113 if s := filepath.Clean(test.result); s != test.result {
114 t.Errorf("Clean(%q) = %q, want %q", test.result, s, test.result)
115 }
116 }
117
118 if testing.Short() {
119 t.Skip("skipping malloc count in short mode")
120 }
121 if runtime.GOMAXPROCS(0) > 1 {
122 t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1")
123 return
124 }
125
126 for _, test := range tests {
127 allocs := testing.AllocsPerRun(100, func() { filepath.Clean(test.result) })
128 if allocs > 0 {
129 t.Errorf("Clean(%q): %v allocs, want zero", test.result, allocs)
130 }
131 }
132 }
133
134 const sep = filepath.Separator
135
136 var slashtests = []PathTest{
137 {"", ""},
138 {"/", string(sep)},
139 {"/a/b", string([]byte{sep, 'a', sep, 'b'})},
140 {"a//b", string([]byte{'a', sep, sep, 'b'})},
141 }
142
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)
147 }
148 if s := filepath.ToSlash(test.result); s != test.path {
149 t.Errorf("ToSlash(%q) = %q, want %q", test.result, s, test.path)
150 }
151 }
152 }
153
154 type SplitListTest struct {
155 list string
156 result []string
157 }
158
159 const lsep = filepath.ListSeparator
160
161 var splitlisttests = []SplitListTest{
162 {"", []string{}},
163 {string([]byte{'a', lsep, 'b'}), []string{"a", "b"}},
164 {string([]byte{lsep, 'a', lsep, 'b'}), []string{"", "a", "b"}},
165 }
166
167 var winsplitlisttests = []SplitListTest{
168
169 {`"a"`, []string{`a`}},
170
171
172 {`";"`, []string{`;`}},
173 {`"a;b"`, []string{`a;b`}},
174 {`";";`, []string{`;`, ``}},
175 {`;";"`, []string{``, `;`}},
176
177
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`}},
187 }
188
189 func TestSplitList(t *testing.T) {
190 tests := splitlisttests
191 if runtime.GOOS == "windows" {
192 tests = append(tests, winsplitlisttests...)
193 }
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)
197 }
198 }
199 }
200
201 type SplitTest struct {
202 path, dir, file string
203 }
204
205 var unixsplittests = []SplitTest{
206 {"a/b", "a/", "b"},
207 {"a/b/", "a/b/", ""},
208 {"a/", "a/", ""},
209 {"a", "", "a"},
210 {"/", "/", ""},
211 }
212
213 var winsplittests = []SplitTest{
214 {`c:`, `c:`, ``},
215 {`c:/`, `c:/`, ``},
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`},
224 }
225
226 func TestSplit(t *testing.T) {
227 var splittests []SplitTest
228 splittests = unixsplittests
229 if runtime.GOOS == "windows" {
230 splittests = append(splittests, winsplittests...)
231 }
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)
235 }
236 }
237 }
238
239 type JoinTest struct {
240 elem []string
241 path string
242 }
243
244 var jointests = []JoinTest{
245
246 {[]string{}, ""},
247
248
249 {[]string{""}, ""},
250 {[]string{"/"}, "/"},
251 {[]string{"a"}, "a"},
252
253
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{"", ""}, ""},
265
266
267 {[]string{"/", "a", "b"}, "/a/b"},
268 }
269
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`},
297 }
298
299 func TestJoin(t *testing.T) {
300 if runtime.GOOS == "windows" {
301 jointests = append(jointests, winjointests...)
302 }
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)
307 }
308 }
309 }
310
311 type ExtTest struct {
312 path, ext string
313 }
314
315 var exttests = []ExtTest{
316 {"path.go", ".go"},
317 {"path.pb.go", ".go"},
318 {"a.dir/b", ""},
319 {"a.dir/b.go", ".go"},
320 {"a.dir/", ""},
321 }
322
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)
327 }
328 }
329 }
330
331 type Node struct {
332 name string
333 entries []*Node
334 mark int
335 }
336
337 var tree = &Node{
338 "testdata",
339 []*Node{
340 {"a", nil, 0},
341 {"b", []*Node{}, 0},
342 {"c", nil, 0},
343 {
344 "d",
345 []*Node{
346 {"x", nil, 0},
347 {"y", []*Node{}, 0},
348 {
349 "z",
350 []*Node{
351 {"u", nil, 0},
352 {"v", nil, 0},
353 },
354 0,
355 },
356 },
357 0,
358 },
359 },
360 0,
361 }
362
363 func walkTree(n *Node, path string, f func(path string, n *Node)) {
364 f(path, n)
365 for _, e := range n.entries {
366 walkTree(e, filepath.Join(path, e.name), f)
367 }
368 }
369
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)
374 if err != nil {
375 t.Errorf("makeTree: %v", err)
376 return
377 }
378 fd.Close()
379 } else {
380 os.Mkdir(path, 0770)
381 }
382 })
383 }
384
385 func markTree(n *Node) { walkTree(n, "", func(path string, n *Node) { n.mark++ }) }
386
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)
391 }
392 n.mark = 0
393 })
394 }
395
396
397
398
399 func mark(d fs.DirEntry, err error, errors *[]error, clear bool) error {
400 name := d.Name()
401 walkTree(tree, tree.name, func(path string, n *Node) {
402 if n.name == name {
403 n.mark++
404 }
405 })
406 if err != nil {
407 *errors = append(*errors, err)
408 if clear {
409 return nil
410 }
411 return err
412 }
413 return nil
414 }
415
416
417
418 func chdir(t *testing.T, dir string) {
419 olddir, err := os.Getwd()
420 if err != nil {
421 t.Fatalf("getwd %s: %v", dir, err)
422 }
423 if err := os.Chdir(dir); err != nil {
424 t.Fatalf("chdir %s: %v", dir, err)
425 }
426
427 t.Cleanup(func() {
428 if err := os.Chdir(olddir); err != nil {
429 t.Errorf("restore original working directory %s: %v", olddir, err)
430 os.Exit(1)
431 }
432 })
433 }
434
435 func chtmpdir(t *testing.T) (restore func()) {
436 oldwd, err := os.Getwd()
437 if err != nil {
438 t.Fatalf("chtmpdir: %v", err)
439 }
440 d, err := os.MkdirTemp("", "test")
441 if err != nil {
442 t.Fatalf("chtmpdir: %v", err)
443 }
444 if err := os.Chdir(d); err != nil {
445 t.Fatalf("chtmpdir: %v", err)
446 }
447 return func() {
448 if err := os.Chdir(oldwd); err != nil {
449 t.Fatalf("chtmpdir: %v", err)
450 }
451 os.RemoveAll(d)
452 }
453 }
454
455
456
457 func tempDirCanonical(t *testing.T) string {
458 dir := t.TempDir()
459
460 cdir, err := filepath.EvalSymlinks(dir)
461 if err != nil {
462 t.Errorf("tempDirCanonical: %v", err)
463 }
464
465 return cdir
466 }
467
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)
472 })
473 }
474 testWalk(t, walk, 1)
475 }
476
477 type statDirEntry struct {
478 info fs.FileInfo
479 }
480
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 }
485
486 func TestWalkDir(t *testing.T) {
487 testWalk(t, filepath.WalkDir, 2)
488 }
489
490 func testWalk(t *testing.T, walk func(string, fs.WalkDirFunc) error, errVisit int) {
491 if runtime.GOOS == "ios" {
492 restore := chtmpdir(t)
493 defer restore()
494 }
495
496 tmpDir := t.TempDir()
497
498 origDir, err := os.Getwd()
499 if err != nil {
500 t.Fatal("finding working dir:", err)
501 }
502 if err = os.Chdir(tmpDir); err != nil {
503 t.Fatal("entering temp dir:", err)
504 }
505 defer os.Chdir(origDir)
506
507 makeTree(t)
508 errors := make([]error, 0, 10)
509 clear := true
510 markFn := func(path string, d fs.DirEntry, err error) error {
511 return mark(d, err, &errors, clear)
512 }
513
514 err = walk(tree.name, markFn)
515 if err != nil {
516 t.Fatalf("no error expected, found: %s", err)
517 }
518 if len(errors) != 0 {
519 t.Fatalf("unexpected errors: %s", errors)
520 }
521 checkMarks(t, true)
522 errors = errors[0:0]
523
524 t.Run("PermErr", func(t *testing.T) {
525
526
527
528 if runtime.GOOS == "windows" {
529 t.Skip("skipping on Windows")
530 }
531 if os.Getuid() == 0 {
532 t.Skip("skipping as root")
533 }
534 if testing.Short() {
535 t.Skip("skipping in short mode")
536 }
537
538
539 os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0)
540 os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0)
541
542
543
544 markTree(tree.entries[1])
545 markTree(tree.entries[3])
546
547 tree.entries[1].mark -= errVisit
548 tree.entries[3].mark -= errVisit
549 err := walk(tree.name, markFn)
550 if err != nil {
551 t.Fatalf("expected no error return from Walk, got %s", err)
552 }
553 if len(errors) != 2 {
554 t.Errorf("expected 2 errors, got %d: %s", len(errors), errors)
555 }
556
557 checkMarks(t, true)
558 errors = errors[0:0]
559
560
561
562 markTree(tree.entries[1])
563 markTree(tree.entries[3])
564
565 tree.entries[1].mark -= errVisit
566 tree.entries[3].mark -= errVisit
567 clear = false
568 err = walk(tree.name, markFn)
569 if err == nil {
570 t.Fatalf("expected error return from Walk")
571 }
572 if len(errors) != 1 {
573 t.Errorf("expected 1 error, got %d: %s", len(errors), errors)
574 }
575
576 checkMarks(t, false)
577 errors = errors[0:0]
578
579
580 os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770)
581 os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770)
582 })
583 }
584
585 func touch(t *testing.T, name string) {
586 f, err := os.Create(name)
587 if err != nil {
588 t.Fatal(err)
589 }
590 if err := f.Close(); err != nil {
591 t.Fatal(err)
592 }
593 }
594
595 func TestWalkSkipDirOnFile(t *testing.T) {
596 td := t.TempDir()
597
598 if err := os.MkdirAll(filepath.Join(td, "dir"), 0755); err != nil {
599 t.Fatal(err)
600 }
601 touch(t, filepath.Join(td, "dir/foo1"))
602 touch(t, filepath.Join(td, "dir/foo2"))
603
604 sawFoo2 := false
605 walker := func(path string) error {
606 if strings.HasSuffix(path, "foo2") {
607 sawFoo2 = true
608 }
609 if strings.HasSuffix(path, "foo1") {
610 return filepath.SkipDir
611 }
612 return nil
613 }
614 walkFn := func(path string, _ fs.FileInfo, _ error) error { return walker(path) }
615 walkDirFn := func(path string, _ fs.DirEntry, _ error) error { return walker(path) }
616
617 check := func(t *testing.T, walk func(root string) error, root string) {
618 t.Helper()
619 sawFoo2 = false
620 err := walk(root)
621 if err != nil {
622 t.Fatal(err)
623 }
624 if sawFoo2 {
625 t.Errorf("SkipDir on file foo1 did not block processing of foo2")
626 }
627 }
628
629 t.Run("Walk", func(t *testing.T) {
630 Walk := func(root string) error { return filepath.Walk(td, walkFn) }
631 check(t, Walk, td)
632 check(t, Walk, filepath.Join(td, "dir"))
633 })
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"))
638 })
639 }
640
641 func TestWalkFileError(t *testing.T) {
642 td := t.TempDir()
643
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 {
648 t.Fatal(err)
649 }
650 touch(t, filepath.Join(dir, "baz"))
651 touch(t, filepath.Join(dir, "stat-error"))
652 defer func() {
653 *filepath.LstatP = os.Lstat
654 }()
655 statErr := errors.New("some stat error")
656 *filepath.LstatP = func(path string) (fs.FileInfo, error) {
657 if strings.HasSuffix(path, "stat-error") {
658 return nil, statErr
659 }
660 return os.Lstat(path)
661 }
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
666 return nil
667 })
668 if err != nil {
669 t.Errorf("Walk error: %v", err)
670 }
671 want := map[string]error{
672 ".": nil,
673 "foo": nil,
674 "bar": nil,
675 "dir": nil,
676 "dir/baz": nil,
677 "dir/stat-error": statErr,
678 }
679 if !reflect.DeepEqual(got, want) {
680 t.Errorf("Walked %#v; want %#v", got, want)
681 }
682 }
683
684 var basetests = []PathTest{
685 {"", "."},
686 {".", "."},
687 {"/.", "."},
688 {"/", "/"},
689 {"////", "/"},
690 {"x/", "x"},
691 {"abc", "abc"},
692 {"abc/def", "def"},
693 {"a/b/.x", ".x"},
694 {"a/b/c.", "c."},
695 {"a/b/c.x", "c.x"},
696 }
697
698 var winbasetests = []PathTest{
699 {`c:\`, `\`},
700 {`c:.`, `.`},
701 {`c:\a\b`, `b`},
702 {`c:a\b`, `b`},
703 {`c:a\b\c`, `c`},
704 {`\\host\share\`, `\`},
705 {`\\host\share\a`, `a`},
706 {`\\host\share\a\b`, `b`},
707 }
708
709 func TestBase(t *testing.T) {
710 tests := basetests
711 if runtime.GOOS == "windows" {
712
713 for i := range tests {
714 tests[i].result = filepath.Clean(tests[i].result)
715 }
716
717 tests = append(tests, winbasetests...)
718 }
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)
722 }
723 }
724 }
725
726 var dirtests = []PathTest{
727 {"", "."},
728 {".", "."},
729 {"/.", "/"},
730 {"/", "/"},
731 {"////", "/"},
732 {"/foo", "/"},
733 {"x/", "x"},
734 {"abc", "."},
735 {"abc/def", "abc"},
736 {"a/b/.x", "a/b"},
737 {"a/b/c.", "a/b"},
738 {"a/b/c.x", "a/b"},
739 }
740
741 var windirtests = []PathTest{
742 {`c:\`, `c:\`},
743 {`c:.`, `c:.`},
744 {`c:\a\b`, `c:\a`},
745 {`c:a\b`, `c:a`},
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`},
751 }
752
753 func TestDir(t *testing.T) {
754 tests := dirtests
755 if runtime.GOOS == "windows" {
756
757 for i := range tests {
758 tests[i].result = filepath.Clean(tests[i].result)
759 }
760
761 tests = append(tests, windirtests...)
762 }
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)
766 }
767 }
768 }
769
770 type IsAbsTest struct {
771 path string
772 isAbs bool
773 }
774
775 var isabstests = []IsAbsTest{
776 {"", false},
777 {"/", true},
778 {"/usr/bin/gcc", true},
779 {"..", false},
780 {"/a/../bb", true},
781 {".", false},
782 {"./", false},
783 {"lala", false},
784 }
785
786 var winisabstests = []IsAbsTest{
787 {`C:\`, true},
788 {`c\`, false},
789 {`c::`, false},
790 {`c:`, false},
791 {`/`, false},
792 {`\`, false},
793 {`\Windows`, false},
794 {`c:a\b`, false},
795 {`c:\a\b`, true},
796 {`c:/a/b`, true},
797 {`\\host\share`, true},
798 {`\\host\share\`, true},
799 {`\\host\share\foo`, true},
800 {`//host/share/foo/bar`, true},
801 }
802
803 func TestIsAbs(t *testing.T) {
804 var tests []IsAbsTest
805 if runtime.GOOS == "windows" {
806 tests = append(tests, winisabstests...)
807
808 for _, test := range isabstests {
809 tests = append(tests, IsAbsTest{test.path, false})
810 }
811
812 for _, test := range isabstests {
813 tests = append(tests, IsAbsTest{"c:" + test.path, test.isAbs})
814 }
815
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})
820 } else {
821 tests = isabstests
822 }
823
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)
827 }
828 }
829 }
830
831 type EvalSymlinksTest struct {
832
833 path, dest string
834 }
835
836 var EvalSymlinksTestDirs = []EvalSymlinksTest{
837 {"test", ""},
838 {"test/dir", ""},
839 {"test/dir/link3", "../../"},
840 {"test/link1", "../test"},
841 {"test/link2", "dir"},
842 {"test/linkabs", "/"},
843 {"test/link4", "../test2"},
844 {"test2", "test/dir"},
845
846 {"src", ""},
847 {"src/pool", ""},
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"},
855 }
856
857 var EvalSymlinksTests = []EvalSymlinksTest{
858 {"test", "test"},
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"},
870 }
871
872
873
874 func simpleJoin(dir, path string) string {
875 return dir + string(filepath.Separator) + path
876 }
877
878 func testEvalSymlinks(t *testing.T, path, want string) {
879 have, err := filepath.EvalSymlinks(path)
880 if err != nil {
881 t.Errorf("EvalSymlinks(%q) error: %v", path, err)
882 return
883 }
884 if filepath.Clean(have) != filepath.Clean(want) {
885 t.Errorf("EvalSymlinks(%q) returns %q, want %q", path, have, want)
886 }
887 }
888
889 func testEvalSymlinksAfterChdir(t *testing.T, wd, path, want string) {
890 cwd, err := os.Getwd()
891 if err != nil {
892 t.Fatal(err)
893 }
894 defer func() {
895 err := os.Chdir(cwd)
896 if err != nil {
897 t.Fatal(err)
898 }
899 }()
900
901 err = os.Chdir(wd)
902 if err != nil {
903 t.Fatal(err)
904 }
905
906 have, err := filepath.EvalSymlinks(path)
907 if err != nil {
908 t.Errorf("EvalSymlinks(%q) in %q directory error: %v", path, wd, err)
909 return
910 }
911 if filepath.Clean(have) != filepath.Clean(want) {
912 t.Errorf("EvalSymlinks(%q) in %q directory returns %q, want %q", path, wd, have, want)
913 }
914 }
915
916 func TestEvalSymlinks(t *testing.T) {
917 testenv.MustHaveSymlink(t)
918
919 tmpDir := t.TempDir()
920
921
922
923 var err error
924 tmpDir, err = filepath.EvalSymlinks(tmpDir)
925 if err != nil {
926 t.Fatal("eval symlink for tmp dir:", err)
927 }
928
929
930 for _, d := range EvalSymlinksTestDirs {
931 var err error
932 path := simpleJoin(tmpDir, d.path)
933 if d.dest == "" {
934 err = os.Mkdir(path, 0755)
935 } else {
936 err = os.Symlink(d.dest, path)
937 }
938 if err != nil {
939 t.Fatal(err)
940 }
941 }
942
943
944 for _, test := range EvalSymlinksTests {
945 path := simpleJoin(tmpDir, test.path)
946
947 dest := simpleJoin(tmpDir, test.dest)
948 if filepath.IsAbs(test.dest) || os.IsPathSeparator(test.dest[0]) {
949 dest = test.dest
950 }
951 testEvalSymlinks(t, path, dest)
952
953
954 testEvalSymlinksAfterChdir(t, path, ".", ".")
955
956
957 if runtime.GOOS == "windows" {
958 volDot := filepath.VolumeName(tmpDir) + "."
959 testEvalSymlinksAfterChdir(t, path, volDot, volDot)
960 }
961
962
963 dotdotPath := simpleJoin("..", test.dest)
964 if filepath.IsAbs(test.dest) || os.IsPathSeparator(test.dest[0]) {
965 dotdotPath = test.dest
966 }
967 testEvalSymlinksAfterChdir(t,
968 simpleJoin(tmpDir, "test"),
969 simpleJoin("..", test.path),
970 dotdotPath)
971
972
973 testEvalSymlinksAfterChdir(t, tmpDir, test.path, test.dest)
974 }
975 }
976
977 func TestEvalSymlinksIsNotExist(t *testing.T) {
978 testenv.MustHaveSymlink(t)
979
980 defer chtmpdir(t)()
981
982 _, err := filepath.EvalSymlinks("notexist")
983 if !os.IsNotExist(err) {
984 t.Errorf("expected the file is not found, got %v\n", err)
985 }
986
987 err = os.Symlink("notexist", "link")
988 if err != nil {
989 t.Fatal(err)
990 }
991 defer os.Remove("link")
992
993 _, err = filepath.EvalSymlinks("link")
994 if !os.IsNotExist(err) {
995 t.Errorf("expected the file is not found, got %v\n", err)
996 }
997 }
998
999 func TestIssue13582(t *testing.T) {
1000 testenv.MustHaveSymlink(t)
1001
1002 tmpDir := t.TempDir()
1003
1004 dir := filepath.Join(tmpDir, "dir")
1005 err := os.Mkdir(dir, 0755)
1006 if err != nil {
1007 t.Fatal(err)
1008 }
1009 linkToDir := filepath.Join(tmpDir, "link_to_dir")
1010 err = os.Symlink(dir, linkToDir)
1011 if err != nil {
1012 t.Fatal(err)
1013 }
1014 file := filepath.Join(linkToDir, "file")
1015 err = os.WriteFile(file, nil, 0644)
1016 if err != nil {
1017 t.Fatal(err)
1018 }
1019 link1 := filepath.Join(linkToDir, "link1")
1020 err = os.Symlink(file, link1)
1021 if err != nil {
1022 t.Fatal(err)
1023 }
1024 link2 := filepath.Join(linkToDir, "link2")
1025 err = os.Symlink(link1, link2)
1026 if err != nil {
1027 t.Fatal(err)
1028 }
1029
1030
1031 realTmpDir, err := filepath.EvalSymlinks(tmpDir)
1032 if err != nil {
1033 t.Fatal(err)
1034 }
1035 realDir := filepath.Join(realTmpDir, "dir")
1036 realFile := filepath.Join(realDir, "file")
1037
1038 tests := []struct {
1039 path, want string
1040 }{
1041 {dir, realDir},
1042 {linkToDir, realDir},
1043 {file, realFile},
1044 {link1, realFile},
1045 {link2, realFile},
1046 }
1047 for i, test := range tests {
1048 have, err := filepath.EvalSymlinks(test.path)
1049 if err != nil {
1050 t.Fatal(err)
1051 }
1052 if have != test.want {
1053 t.Errorf("test#%d: EvalSymlinks(%q) returns %q, want %q", i, test.path, have, test.want)
1054 }
1055 }
1056 }
1057
1058
1059
1060 var absTestDirs = []string{
1061 "a",
1062 "a/b",
1063 "a/b/c",
1064 }
1065
1066
1067
1068
1069 var absTests = []string{
1070 ".",
1071 "b",
1072 "b/",
1073 "../a",
1074 "../a/b",
1075 "../a/b/./c/../../.././a",
1076 "../a/b/./c/../../.././a/",
1077 "$",
1078 "$/.",
1079 "$/a/../a/b",
1080 "$/a/b/c/../../.././a",
1081 "$/a/b/c/../../.././a/",
1082 }
1083
1084 func TestAbs(t *testing.T) {
1085 root := t.TempDir()
1086 wd, err := os.Getwd()
1087 if err != nil {
1088 t.Fatal("getwd failed: ", err)
1089 }
1090 err = os.Chdir(root)
1091 if err != nil {
1092 t.Fatal("chdir failed: ", err)
1093 }
1094 defer os.Chdir(wd)
1095
1096 for _, dir := range absTestDirs {
1097 err = os.Mkdir(dir, 0777)
1098 if err != nil {
1099 t.Fatal("Mkdir failed: ", err)
1100 }
1101 }
1102
1103 if runtime.GOOS == "windows" {
1104 vol := filepath.VolumeName(root)
1105 var extra []string
1106 for _, path := range absTests {
1107 if strings.Contains(path, "$") {
1108 continue
1109 }
1110 path = vol + path
1111 extra = append(extra, path)
1112 }
1113 absTests = append(absTests, extra...)
1114 }
1115
1116 err = os.Chdir(absTestDirs[0])
1117 if err != nil {
1118 t.Fatal("chdir failed: ", err)
1119 }
1120
1121 for _, path := range absTests {
1122 path = strings.ReplaceAll(path, "$", root)
1123 info, err := os.Stat(path)
1124 if err != nil {
1125 t.Errorf("%s: %s", path, err)
1126 continue
1127 }
1128
1129 abspath, err := filepath.Abs(path)
1130 if err != nil {
1131 t.Errorf("Abs(%q) error: %v", path, err)
1132 continue
1133 }
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)
1137 }
1138 if !filepath.IsAbs(abspath) {
1139 t.Errorf("Abs(%q)=%q, not an absolute path", path, abspath)
1140 }
1141 if filepath.IsAbs(abspath) && abspath != filepath.Clean(abspath) {
1142 t.Errorf("Abs(%q)=%q, isn't clean", path, abspath)
1143 }
1144 }
1145 }
1146
1147
1148
1149
1150 func TestAbsEmptyString(t *testing.T) {
1151 root := t.TempDir()
1152
1153 wd, err := os.Getwd()
1154 if err != nil {
1155 t.Fatal("getwd failed: ", err)
1156 }
1157 err = os.Chdir(root)
1158 if err != nil {
1159 t.Fatal("chdir failed: ", err)
1160 }
1161 defer os.Chdir(wd)
1162
1163 info, err := os.Stat(root)
1164 if err != nil {
1165 t.Fatalf("%s: %s", root, err)
1166 }
1167
1168 abspath, err := filepath.Abs("")
1169 if err != nil {
1170 t.Fatalf(`Abs("") error: %v`, err)
1171 }
1172 absinfo, err := os.Stat(abspath)
1173 if err != nil || !os.SameFile(absinfo, info) {
1174 t.Errorf(`Abs("")=%q, not the same file`, abspath)
1175 }
1176 if !filepath.IsAbs(abspath) {
1177 t.Errorf(`Abs("")=%q, not an absolute path`, abspath)
1178 }
1179 if filepath.IsAbs(abspath) && abspath != filepath.Clean(abspath) {
1180 t.Errorf(`Abs("")=%q, isn't clean`, abspath)
1181 }
1182 }
1183
1184 type RelTests struct {
1185 root, path, want string
1186 }
1187
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"},
1222 {".", "..", ".."},
1223
1224
1225 {"..", ".", "err"},
1226 {"..", "a", "err"},
1227 {"../..", "..", "err"},
1228 {"a", "/a", "err"},
1229 {"/a", "a", "err"},
1230 }
1231
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`},
1240 }
1241
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)
1247 }
1248 tests = append(tests, winreltests...)
1249 }
1250 for _, test := range tests {
1251 got, err := filepath.Rel(test.root, test.path)
1252 if test.want == "err" {
1253 if err == nil {
1254 t.Errorf("Rel(%q, %q)=%q, want error", test.root, test.path, got)
1255 }
1256 continue
1257 }
1258 if err != nil {
1259 t.Errorf("Rel(%q, %q): want %q, got error: %s", test.root, test.path, test.want, err)
1260 }
1261 if got != test.want {
1262 t.Errorf("Rel(%q, %q)=%q, want %q", test.root, test.path, got, test.want)
1263 }
1264 }
1265 }
1266
1267 type VolumeNameTest struct {
1268 path string
1269 vol string
1270 }
1271
1272 var volumenametests = []VolumeNameTest{
1273 {`c:/foo/bar`, `c:`},
1274 {`c:`, `c:`},
1275 {`2:`, ``},
1276 {``, ``},
1277 {`\\\host`, ``},
1278 {`\\\host\`, ``},
1279 {`\\\host\share`, ``},
1280 {`\\\host\\share`, ``},
1281 {`\\host`, ``},
1282 {`//host`, ``},
1283 {`\\host\`, ``},
1284 {`//host/`, ``},
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`},
1295 }
1296
1297 func TestVolumeName(t *testing.T) {
1298 if runtime.GOOS != "windows" {
1299 return
1300 }
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)
1304 }
1305 }
1306 }
1307
1308 func TestDriveLetterInEvalSymlinks(t *testing.T) {
1309 if runtime.GOOS != "windows" {
1310 return
1311 }
1312 wd, _ := os.Getwd()
1313 if len(wd) < 3 {
1314 t.Errorf("Current directory path %q is too short", wd)
1315 }
1316 lp := strings.ToLower(wd)
1317 up := strings.ToUpper(wd)
1318 flp, err := filepath.EvalSymlinks(lp)
1319 if err != nil {
1320 t.Fatalf("EvalSymlinks(%q) failed: %q", lp, err)
1321 }
1322 fup, err := filepath.EvalSymlinks(up)
1323 if err != nil {
1324 t.Fatalf("EvalSymlinks(%q) failed: %q", up, err)
1325 }
1326 if flp != fup {
1327 t.Errorf("Results of EvalSymlinks do not match: %q and %q", flp, fup)
1328 }
1329 }
1330
1331 func TestBug3486(t *testing.T) {
1332 if runtime.GOOS == "ios" {
1333 t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
1334 }
1335 root, err := filepath.EvalSymlinks(runtime.GOROOT() + "/test")
1336 if err != nil {
1337 t.Fatal(err)
1338 }
1339 bugs := filepath.Join(root, "fixedbugs")
1340 ken := filepath.Join(root, "ken")
1341 seenBugs := false
1342 seenKen := false
1343 err = filepath.Walk(root, func(pth string, info fs.FileInfo, err error) error {
1344 if err != nil {
1345 t.Fatal(err)
1346 }
1347
1348 switch pth {
1349 case bugs:
1350 seenBugs = true
1351 return filepath.SkipDir
1352 case ken:
1353 if !seenBugs {
1354 t.Fatal("filepath.Walk out of order - ken before fixedbugs")
1355 }
1356 seenKen = true
1357 }
1358 return nil
1359 })
1360 if err != nil {
1361 t.Fatal(err)
1362 }
1363 if !seenKen {
1364 t.Fatalf("%q not seen", ken)
1365 }
1366 }
1367
1368 func testWalkSymlink(t *testing.T, mklink func(target, link string) error) {
1369 tmpdir := t.TempDir()
1370
1371 wd, err := os.Getwd()
1372 if err != nil {
1373 t.Fatal(err)
1374 }
1375 defer os.Chdir(wd)
1376
1377 err = os.Chdir(tmpdir)
1378 if err != nil {
1379 t.Fatal(err)
1380 }
1381
1382 err = mklink(tmpdir, "link")
1383 if err != nil {
1384 t.Fatal(err)
1385 }
1386
1387 var visited []string
1388 err = filepath.Walk(tmpdir, func(path string, info fs.FileInfo, err error) error {
1389 if err != nil {
1390 t.Fatal(err)
1391 }
1392 rel, err := filepath.Rel(tmpdir, path)
1393 if err != nil {
1394 t.Fatal(err)
1395 }
1396 visited = append(visited, rel)
1397 return nil
1398 })
1399 if err != nil {
1400 t.Fatal(err)
1401 }
1402 sort.Strings(visited)
1403 want := []string{".", "link"}
1404 if fmt.Sprintf("%q", visited) != fmt.Sprintf("%q", want) {
1405 t.Errorf("unexpected paths visited %q, want %q", visited, want)
1406 }
1407 }
1408
1409 func TestWalkSymlink(t *testing.T) {
1410 testenv.MustHaveSymlink(t)
1411 testWalkSymlink(t, os.Symlink)
1412 }
1413
1414 func TestIssue29372(t *testing.T) {
1415 tmpDir := t.TempDir()
1416
1417 path := filepath.Join(tmpDir, "file.txt")
1418 err := os.WriteFile(path, nil, 0644)
1419 if err != nil {
1420 t.Fatal(err)
1421 }
1422
1423 pathSeparator := string(filepath.Separator)
1424 tests := []string{
1425 path + strings.Repeat(pathSeparator, 1),
1426 path + strings.Repeat(pathSeparator, 2),
1427 path + strings.Repeat(pathSeparator, 1) + ".",
1428 path + strings.Repeat(pathSeparator, 2) + ".",
1429 path + strings.Repeat(pathSeparator, 1) + "..",
1430 path + strings.Repeat(pathSeparator, 2) + "..",
1431 }
1432
1433 for i, test := range tests {
1434 _, err = filepath.EvalSymlinks(test)
1435 if err != syscall.ENOTDIR {
1436 t.Fatalf("test#%d: want %q, got %q", i, syscall.ENOTDIR, err)
1437 }
1438 }
1439 }
1440
1441
1442 func TestEvalSymlinksAboveRoot(t *testing.T) {
1443 testenv.MustHaveSymlink(t)
1444
1445 t.Parallel()
1446
1447 tmpDir := t.TempDir()
1448
1449 evalTmpDir, err := filepath.EvalSymlinks(tmpDir)
1450 if err != nil {
1451 t.Fatal(err)
1452 }
1453
1454 if err := os.Mkdir(filepath.Join(evalTmpDir, "a"), 0777); err != nil {
1455 t.Fatal(err)
1456 }
1457 if err := os.Symlink(filepath.Join(evalTmpDir, "a"), filepath.Join(evalTmpDir, "b")); err != nil {
1458 t.Fatal(err)
1459 }
1460 if err := os.WriteFile(filepath.Join(evalTmpDir, "a", "file"), nil, 0666); err != nil {
1461 t.Fatal(err)
1462 }
1463
1464
1465 vol := filepath.VolumeName(evalTmpDir)
1466 c := strings.Count(evalTmpDir[len(vol):], string(os.PathSeparator))
1467 var dd []string
1468 for i := 0; i < c+2; i++ {
1469 dd = append(dd, "..")
1470 }
1471
1472 wantSuffix := strings.Join([]string{"a", "file"}, string(os.PathSeparator))
1473
1474
1475 for _, i := range []int{c, c + 1, c + 2} {
1476 check := strings.Join([]string{evalTmpDir, strings.Join(dd[:i], string(os.PathSeparator)), evalTmpDir[len(vol)+1:], "b", "file"}, string(os.PathSeparator))
1477 resolved, err := filepath.EvalSymlinks(check)
1478 switch {
1479 case runtime.GOOS == "darwin" && errors.Is(err, fs.ErrNotExist):
1480
1481 testenv.SkipFlaky(t, 37910)
1482 case err != nil:
1483 t.Errorf("EvalSymlinks(%q) failed: %v", check, err)
1484 case !strings.HasSuffix(resolved, wantSuffix):
1485 t.Errorf("EvalSymlinks(%q) = %q does not end with %q", check, resolved, wantSuffix)
1486 default:
1487 t.Logf("EvalSymlinks(%q) = %q", check, resolved)
1488 }
1489 }
1490 }
1491
1492
1493 func TestEvalSymlinksAboveRootChdir(t *testing.T) {
1494 testenv.MustHaveSymlink(t)
1495
1496 tmpDir, err := os.MkdirTemp("", "TestEvalSymlinksAboveRootChdir")
1497 if err != nil {
1498 t.Fatal(err)
1499 }
1500 defer os.RemoveAll(tmpDir)
1501 chdir(t, tmpDir)
1502
1503 subdir := filepath.Join("a", "b")
1504 if err := os.MkdirAll(subdir, 0777); err != nil {
1505 t.Fatal(err)
1506 }
1507 if err := os.Symlink(subdir, "c"); err != nil {
1508 t.Fatal(err)
1509 }
1510 if err := os.WriteFile(filepath.Join(subdir, "file"), nil, 0666); err != nil {
1511 t.Fatal(err)
1512 }
1513
1514 subdir = filepath.Join("d", "e", "f")
1515 if err := os.MkdirAll(subdir, 0777); err != nil {
1516 t.Fatal(err)
1517 }
1518 if err := os.Chdir(subdir); err != nil {
1519 t.Fatal(err)
1520 }
1521
1522 check := filepath.Join("..", "..", "..", "c", "file")
1523 wantSuffix := filepath.Join("a", "b", "file")
1524 if resolved, err := filepath.EvalSymlinks(check); err != nil {
1525 t.Errorf("EvalSymlinks(%q) failed: %v", check, err)
1526 } else if !strings.HasSuffix(resolved, wantSuffix) {
1527 t.Errorf("EvalSymlinks(%q) = %q does not end with %q", check, resolved, wantSuffix)
1528 } else {
1529 t.Logf("EvalSymlinks(%q) = %q", check, resolved)
1530 }
1531 }
1532
View as plain text