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\foo`, true},
798 {`//host/share/foo/bar`, true},
799 }
800
801 func TestIsAbs(t *testing.T) {
802 var tests []IsAbsTest
803 if runtime.GOOS == "windows" {
804 tests = append(tests, winisabstests...)
805
806 for _, test := range isabstests {
807 tests = append(tests, IsAbsTest{test.path, false})
808 }
809
810 for _, test := range isabstests {
811 tests = append(tests, IsAbsTest{"c:" + test.path, test.isAbs})
812 }
813
814 tests = append(tests, IsAbsTest{os.DevNull, true})
815 tests = append(tests, IsAbsTest{"NUL", true})
816 tests = append(tests, IsAbsTest{"nul", true})
817 tests = append(tests, IsAbsTest{"CON", true})
818 } else {
819 tests = isabstests
820 }
821
822 for _, test := range tests {
823 if r := filepath.IsAbs(test.path); r != test.isAbs {
824 t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs)
825 }
826 }
827 }
828
829 type EvalSymlinksTest struct {
830
831 path, dest string
832 }
833
834 var EvalSymlinksTestDirs = []EvalSymlinksTest{
835 {"test", ""},
836 {"test/dir", ""},
837 {"test/dir/link3", "../../"},
838 {"test/link1", "../test"},
839 {"test/link2", "dir"},
840 {"test/linkabs", "/"},
841 {"test/link4", "../test2"},
842 {"test2", "test/dir"},
843
844 {"src", ""},
845 {"src/pool", ""},
846 {"src/pool/test", ""},
847 {"src/versions", ""},
848 {"src/versions/current", "../../version"},
849 {"src/versions/v1", ""},
850 {"src/versions/v1/modules", ""},
851 {"src/versions/v1/modules/test", "../../../pool/test"},
852 {"version", "src/versions/v1"},
853 }
854
855 var EvalSymlinksTests = []EvalSymlinksTest{
856 {"test", "test"},
857 {"test/dir", "test/dir"},
858 {"test/dir/../..", "."},
859 {"test/link1", "test"},
860 {"test/link2", "test/dir"},
861 {"test/link1/dir", "test/dir"},
862 {"test/link2/..", "test"},
863 {"test/dir/link3", "."},
864 {"test/link2/link3/test", "test"},
865 {"test/linkabs", "/"},
866 {"test/link4/..", "test"},
867 {"src/versions/current/modules/test", "src/pool/test"},
868 }
869
870
871
872 func simpleJoin(dir, path string) string {
873 return dir + string(filepath.Separator) + path
874 }
875
876 func testEvalSymlinks(t *testing.T, path, want string) {
877 have, err := filepath.EvalSymlinks(path)
878 if err != nil {
879 t.Errorf("EvalSymlinks(%q) error: %v", path, err)
880 return
881 }
882 if filepath.Clean(have) != filepath.Clean(want) {
883 t.Errorf("EvalSymlinks(%q) returns %q, want %q", path, have, want)
884 }
885 }
886
887 func testEvalSymlinksAfterChdir(t *testing.T, wd, path, want string) {
888 cwd, err := os.Getwd()
889 if err != nil {
890 t.Fatal(err)
891 }
892 defer func() {
893 err := os.Chdir(cwd)
894 if err != nil {
895 t.Fatal(err)
896 }
897 }()
898
899 err = os.Chdir(wd)
900 if err != nil {
901 t.Fatal(err)
902 }
903
904 have, err := filepath.EvalSymlinks(path)
905 if err != nil {
906 t.Errorf("EvalSymlinks(%q) in %q directory error: %v", path, wd, err)
907 return
908 }
909 if filepath.Clean(have) != filepath.Clean(want) {
910 t.Errorf("EvalSymlinks(%q) in %q directory returns %q, want %q", path, wd, have, want)
911 }
912 }
913
914 func TestEvalSymlinks(t *testing.T) {
915 testenv.MustHaveSymlink(t)
916
917 tmpDir := t.TempDir()
918
919
920
921 var err error
922 tmpDir, err = filepath.EvalSymlinks(tmpDir)
923 if err != nil {
924 t.Fatal("eval symlink for tmp dir:", err)
925 }
926
927
928 for _, d := range EvalSymlinksTestDirs {
929 var err error
930 path := simpleJoin(tmpDir, d.path)
931 if d.dest == "" {
932 err = os.Mkdir(path, 0755)
933 } else {
934 err = os.Symlink(d.dest, path)
935 }
936 if err != nil {
937 t.Fatal(err)
938 }
939 }
940
941
942 for _, test := range EvalSymlinksTests {
943 path := simpleJoin(tmpDir, test.path)
944
945 dest := simpleJoin(tmpDir, test.dest)
946 if filepath.IsAbs(test.dest) || os.IsPathSeparator(test.dest[0]) {
947 dest = test.dest
948 }
949 testEvalSymlinks(t, path, dest)
950
951
952 testEvalSymlinksAfterChdir(t, path, ".", ".")
953
954
955 if runtime.GOOS == "windows" {
956 volDot := filepath.VolumeName(tmpDir) + "."
957 testEvalSymlinksAfterChdir(t, path, volDot, volDot)
958 }
959
960
961 dotdotPath := simpleJoin("..", test.dest)
962 if filepath.IsAbs(test.dest) || os.IsPathSeparator(test.dest[0]) {
963 dotdotPath = test.dest
964 }
965 testEvalSymlinksAfterChdir(t,
966 simpleJoin(tmpDir, "test"),
967 simpleJoin("..", test.path),
968 dotdotPath)
969
970
971 testEvalSymlinksAfterChdir(t, tmpDir, test.path, test.dest)
972 }
973 }
974
975 func TestEvalSymlinksIsNotExist(t *testing.T) {
976 testenv.MustHaveSymlink(t)
977
978 defer chtmpdir(t)()
979
980 _, err := filepath.EvalSymlinks("notexist")
981 if !os.IsNotExist(err) {
982 t.Errorf("expected the file is not found, got %v\n", err)
983 }
984
985 err = os.Symlink("notexist", "link")
986 if err != nil {
987 t.Fatal(err)
988 }
989 defer os.Remove("link")
990
991 _, err = filepath.EvalSymlinks("link")
992 if !os.IsNotExist(err) {
993 t.Errorf("expected the file is not found, got %v\n", err)
994 }
995 }
996
997 func TestIssue13582(t *testing.T) {
998 testenv.MustHaveSymlink(t)
999
1000 tmpDir := t.TempDir()
1001
1002 dir := filepath.Join(tmpDir, "dir")
1003 err := os.Mkdir(dir, 0755)
1004 if err != nil {
1005 t.Fatal(err)
1006 }
1007 linkToDir := filepath.Join(tmpDir, "link_to_dir")
1008 err = os.Symlink(dir, linkToDir)
1009 if err != nil {
1010 t.Fatal(err)
1011 }
1012 file := filepath.Join(linkToDir, "file")
1013 err = os.WriteFile(file, nil, 0644)
1014 if err != nil {
1015 t.Fatal(err)
1016 }
1017 link1 := filepath.Join(linkToDir, "link1")
1018 err = os.Symlink(file, link1)
1019 if err != nil {
1020 t.Fatal(err)
1021 }
1022 link2 := filepath.Join(linkToDir, "link2")
1023 err = os.Symlink(link1, link2)
1024 if err != nil {
1025 t.Fatal(err)
1026 }
1027
1028
1029 realTmpDir, err := filepath.EvalSymlinks(tmpDir)
1030 if err != nil {
1031 t.Fatal(err)
1032 }
1033 realDir := filepath.Join(realTmpDir, "dir")
1034 realFile := filepath.Join(realDir, "file")
1035
1036 tests := []struct {
1037 path, want string
1038 }{
1039 {dir, realDir},
1040 {linkToDir, realDir},
1041 {file, realFile},
1042 {link1, realFile},
1043 {link2, realFile},
1044 }
1045 for i, test := range tests {
1046 have, err := filepath.EvalSymlinks(test.path)
1047 if err != nil {
1048 t.Fatal(err)
1049 }
1050 if have != test.want {
1051 t.Errorf("test#%d: EvalSymlinks(%q) returns %q, want %q", i, test.path, have, test.want)
1052 }
1053 }
1054 }
1055
1056
1057
1058 var absTestDirs = []string{
1059 "a",
1060 "a/b",
1061 "a/b/c",
1062 }
1063
1064
1065
1066
1067 var absTests = []string{
1068 ".",
1069 "b",
1070 "b/",
1071 "../a",
1072 "../a/b",
1073 "../a/b/./c/../../.././a",
1074 "../a/b/./c/../../.././a/",
1075 "$",
1076 "$/.",
1077 "$/a/../a/b",
1078 "$/a/b/c/../../.././a",
1079 "$/a/b/c/../../.././a/",
1080 }
1081
1082 func TestAbs(t *testing.T) {
1083 root := t.TempDir()
1084 wd, err := os.Getwd()
1085 if err != nil {
1086 t.Fatal("getwd failed: ", err)
1087 }
1088 err = os.Chdir(root)
1089 if err != nil {
1090 t.Fatal("chdir failed: ", err)
1091 }
1092 defer os.Chdir(wd)
1093
1094 for _, dir := range absTestDirs {
1095 err = os.Mkdir(dir, 0777)
1096 if err != nil {
1097 t.Fatal("Mkdir failed: ", err)
1098 }
1099 }
1100
1101 if runtime.GOOS == "windows" {
1102 vol := filepath.VolumeName(root)
1103 var extra []string
1104 for _, path := range absTests {
1105 if strings.Contains(path, "$") {
1106 continue
1107 }
1108 path = vol + path
1109 extra = append(extra, path)
1110 }
1111 absTests = append(absTests, extra...)
1112 }
1113
1114 err = os.Chdir(absTestDirs[0])
1115 if err != nil {
1116 t.Fatal("chdir failed: ", err)
1117 }
1118
1119 for _, path := range absTests {
1120 path = strings.ReplaceAll(path, "$", root)
1121 info, err := os.Stat(path)
1122 if err != nil {
1123 t.Errorf("%s: %s", path, err)
1124 continue
1125 }
1126
1127 abspath, err := filepath.Abs(path)
1128 if err != nil {
1129 t.Errorf("Abs(%q) error: %v", path, err)
1130 continue
1131 }
1132 absinfo, err := os.Stat(abspath)
1133 if err != nil || !os.SameFile(absinfo, info) {
1134 t.Errorf("Abs(%q)=%q, not the same file", path, abspath)
1135 }
1136 if !filepath.IsAbs(abspath) {
1137 t.Errorf("Abs(%q)=%q, not an absolute path", path, abspath)
1138 }
1139 if filepath.IsAbs(abspath) && abspath != filepath.Clean(abspath) {
1140 t.Errorf("Abs(%q)=%q, isn't clean", path, abspath)
1141 }
1142 }
1143 }
1144
1145
1146
1147
1148 func TestAbsEmptyString(t *testing.T) {
1149 root := t.TempDir()
1150
1151 wd, err := os.Getwd()
1152 if err != nil {
1153 t.Fatal("getwd failed: ", err)
1154 }
1155 err = os.Chdir(root)
1156 if err != nil {
1157 t.Fatal("chdir failed: ", err)
1158 }
1159 defer os.Chdir(wd)
1160
1161 info, err := os.Stat(root)
1162 if err != nil {
1163 t.Fatalf("%s: %s", root, err)
1164 }
1165
1166 abspath, err := filepath.Abs("")
1167 if err != nil {
1168 t.Fatalf(`Abs("") error: %v`, err)
1169 }
1170 absinfo, err := os.Stat(abspath)
1171 if err != nil || !os.SameFile(absinfo, info) {
1172 t.Errorf(`Abs("")=%q, not the same file`, abspath)
1173 }
1174 if !filepath.IsAbs(abspath) {
1175 t.Errorf(`Abs("")=%q, not an absolute path`, abspath)
1176 }
1177 if filepath.IsAbs(abspath) && abspath != filepath.Clean(abspath) {
1178 t.Errorf(`Abs("")=%q, isn't clean`, abspath)
1179 }
1180 }
1181
1182 type RelTests struct {
1183 root, path, want string
1184 }
1185
1186 var reltests = []RelTests{
1187 {"a/b", "a/b", "."},
1188 {"a/b/.", "a/b", "."},
1189 {"a/b", "a/b/.", "."},
1190 {"./a/b", "a/b", "."},
1191 {"a/b", "./a/b", "."},
1192 {"ab/cd", "ab/cde", "../cde"},
1193 {"ab/cd", "ab/c", "../c"},
1194 {"a/b", "a/b/c/d", "c/d"},
1195 {"a/b", "a/b/../c", "../c"},
1196 {"a/b/../c", "a/b", "../b"},
1197 {"a/b/c", "a/c/d", "../../c/d"},
1198 {"a/b", "c/d", "../../c/d"},
1199 {"a/b/c/d", "a/b", "../.."},
1200 {"a/b/c/d", "a/b/", "../.."},
1201 {"a/b/c/d/", "a/b", "../.."},
1202 {"a/b/c/d/", "a/b/", "../.."},
1203 {"../../a/b", "../../a/b/c/d", "c/d"},
1204 {"/a/b", "/a/b", "."},
1205 {"/a/b/.", "/a/b", "."},
1206 {"/a/b", "/a/b/.", "."},
1207 {"/ab/cd", "/ab/cde", "../cde"},
1208 {"/ab/cd", "/ab/c", "../c"},
1209 {"/a/b", "/a/b/c/d", "c/d"},
1210 {"/a/b", "/a/b/../c", "../c"},
1211 {"/a/b/../c", "/a/b", "../b"},
1212 {"/a/b/c", "/a/c/d", "../../c/d"},
1213 {"/a/b", "/c/d", "../../c/d"},
1214 {"/a/b/c/d", "/a/b", "../.."},
1215 {"/a/b/c/d", "/a/b/", "../.."},
1216 {"/a/b/c/d/", "/a/b", "../.."},
1217 {"/a/b/c/d/", "/a/b/", "../.."},
1218 {"/../../a/b", "/../../a/b/c/d", "c/d"},
1219 {".", "a/b", "a/b"},
1220 {".", "..", ".."},
1221
1222
1223 {"..", ".", "err"},
1224 {"..", "a", "err"},
1225 {"../..", "..", "err"},
1226 {"a", "/a", "err"},
1227 {"/a", "a", "err"},
1228 }
1229
1230 var winreltests = []RelTests{
1231 {`C:a\b\c`, `C:a/b/d`, `..\d`},
1232 {`C:\`, `D:\`, `err`},
1233 {`C:`, `D:`, `err`},
1234 {`C:\Projects`, `c:\projects\src`, `src`},
1235 {`C:\Projects`, `c:\projects`, `.`},
1236 {`C:\Projects\a\..`, `c:\projects`, `.`},
1237 {`\\host\share`, `\\host\share\file.txt`, `file.txt`},
1238 }
1239
1240 func TestRel(t *testing.T) {
1241 tests := append([]RelTests{}, reltests...)
1242 if runtime.GOOS == "windows" {
1243 for i := range tests {
1244 tests[i].want = filepath.FromSlash(tests[i].want)
1245 }
1246 tests = append(tests, winreltests...)
1247 }
1248 for _, test := range tests {
1249 got, err := filepath.Rel(test.root, test.path)
1250 if test.want == "err" {
1251 if err == nil {
1252 t.Errorf("Rel(%q, %q)=%q, want error", test.root, test.path, got)
1253 }
1254 continue
1255 }
1256 if err != nil {
1257 t.Errorf("Rel(%q, %q): want %q, got error: %s", test.root, test.path, test.want, err)
1258 }
1259 if got != test.want {
1260 t.Errorf("Rel(%q, %q)=%q, want %q", test.root, test.path, got, test.want)
1261 }
1262 }
1263 }
1264
1265 type VolumeNameTest struct {
1266 path string
1267 vol string
1268 }
1269
1270 var volumenametests = []VolumeNameTest{
1271 {`c:/foo/bar`, `c:`},
1272 {`c:`, `c:`},
1273 {`2:`, ``},
1274 {``, ``},
1275 {`\\\host`, ``},
1276 {`\\\host\`, ``},
1277 {`\\\host\share`, ``},
1278 {`\\\host\\share`, ``},
1279 {`\\host`, ``},
1280 {`//host`, ``},
1281 {`\\host\`, ``},
1282 {`//host/`, ``},
1283 {`\\host\share`, `\\host\share`},
1284 {`//host/share`, `//host/share`},
1285 {`\\host\share\`, `\\host\share`},
1286 {`//host/share/`, `//host/share`},
1287 {`\\host\share\foo`, `\\host\share`},
1288 {`//host/share/foo`, `//host/share`},
1289 {`\\host\share\\foo\\\bar\\\\baz`, `\\host\share`},
1290 {`//host/share//foo///bar////baz`, `//host/share`},
1291 {`\\host\share\foo\..\bar`, `\\host\share`},
1292 {`//host/share/foo/../bar`, `//host/share`},
1293 }
1294
1295 func TestVolumeName(t *testing.T) {
1296 if runtime.GOOS != "windows" {
1297 return
1298 }
1299 for _, v := range volumenametests {
1300 if vol := filepath.VolumeName(v.path); vol != v.vol {
1301 t.Errorf("VolumeName(%q)=%q, want %q", v.path, vol, v.vol)
1302 }
1303 }
1304 }
1305
1306 func TestDriveLetterInEvalSymlinks(t *testing.T) {
1307 if runtime.GOOS != "windows" {
1308 return
1309 }
1310 wd, _ := os.Getwd()
1311 if len(wd) < 3 {
1312 t.Errorf("Current directory path %q is too short", wd)
1313 }
1314 lp := strings.ToLower(wd)
1315 up := strings.ToUpper(wd)
1316 flp, err := filepath.EvalSymlinks(lp)
1317 if err != nil {
1318 t.Fatalf("EvalSymlinks(%q) failed: %q", lp, err)
1319 }
1320 fup, err := filepath.EvalSymlinks(up)
1321 if err != nil {
1322 t.Fatalf("EvalSymlinks(%q) failed: %q", up, err)
1323 }
1324 if flp != fup {
1325 t.Errorf("Results of EvalSymlinks do not match: %q and %q", flp, fup)
1326 }
1327 }
1328
1329 func TestBug3486(t *testing.T) {
1330 if runtime.GOOS == "ios" {
1331 t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
1332 }
1333 root, err := filepath.EvalSymlinks(runtime.GOROOT() + "/test")
1334 if err != nil {
1335 t.Fatal(err)
1336 }
1337 bugs := filepath.Join(root, "fixedbugs")
1338 ken := filepath.Join(root, "ken")
1339 seenBugs := false
1340 seenKen := false
1341 err = filepath.Walk(root, func(pth string, info fs.FileInfo, err error) error {
1342 if err != nil {
1343 t.Fatal(err)
1344 }
1345
1346 switch pth {
1347 case bugs:
1348 seenBugs = true
1349 return filepath.SkipDir
1350 case ken:
1351 if !seenBugs {
1352 t.Fatal("filepath.Walk out of order - ken before fixedbugs")
1353 }
1354 seenKen = true
1355 }
1356 return nil
1357 })
1358 if err != nil {
1359 t.Fatal(err)
1360 }
1361 if !seenKen {
1362 t.Fatalf("%q not seen", ken)
1363 }
1364 }
1365
1366 func testWalkSymlink(t *testing.T, mklink func(target, link string) error) {
1367 tmpdir := t.TempDir()
1368
1369 wd, err := os.Getwd()
1370 if err != nil {
1371 t.Fatal(err)
1372 }
1373 defer os.Chdir(wd)
1374
1375 err = os.Chdir(tmpdir)
1376 if err != nil {
1377 t.Fatal(err)
1378 }
1379
1380 err = mklink(tmpdir, "link")
1381 if err != nil {
1382 t.Fatal(err)
1383 }
1384
1385 var visited []string
1386 err = filepath.Walk(tmpdir, func(path string, info fs.FileInfo, err error) error {
1387 if err != nil {
1388 t.Fatal(err)
1389 }
1390 rel, err := filepath.Rel(tmpdir, path)
1391 if err != nil {
1392 t.Fatal(err)
1393 }
1394 visited = append(visited, rel)
1395 return nil
1396 })
1397 if err != nil {
1398 t.Fatal(err)
1399 }
1400 sort.Strings(visited)
1401 want := []string{".", "link"}
1402 if fmt.Sprintf("%q", visited) != fmt.Sprintf("%q", want) {
1403 t.Errorf("unexpected paths visited %q, want %q", visited, want)
1404 }
1405 }
1406
1407 func TestWalkSymlink(t *testing.T) {
1408 testenv.MustHaveSymlink(t)
1409 testWalkSymlink(t, os.Symlink)
1410 }
1411
1412 func TestIssue29372(t *testing.T) {
1413 tmpDir := t.TempDir()
1414
1415 path := filepath.Join(tmpDir, "file.txt")
1416 err := os.WriteFile(path, nil, 0644)
1417 if err != nil {
1418 t.Fatal(err)
1419 }
1420
1421 pathSeparator := string(filepath.Separator)
1422 tests := []string{
1423 path + strings.Repeat(pathSeparator, 1),
1424 path + strings.Repeat(pathSeparator, 2),
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 }
1430
1431 for i, test := range tests {
1432 _, err = filepath.EvalSymlinks(test)
1433 if err != syscall.ENOTDIR {
1434 t.Fatalf("test#%d: want %q, got %q", i, syscall.ENOTDIR, err)
1435 }
1436 }
1437 }
1438
1439
1440 func TestEvalSymlinksAboveRoot(t *testing.T) {
1441 testenv.MustHaveSymlink(t)
1442
1443 t.Parallel()
1444
1445 tmpDir := t.TempDir()
1446
1447 evalTmpDir, err := filepath.EvalSymlinks(tmpDir)
1448 if err != nil {
1449 t.Fatal(err)
1450 }
1451
1452 if err := os.Mkdir(filepath.Join(evalTmpDir, "a"), 0777); err != nil {
1453 t.Fatal(err)
1454 }
1455 if err := os.Symlink(filepath.Join(evalTmpDir, "a"), filepath.Join(evalTmpDir, "b")); err != nil {
1456 t.Fatal(err)
1457 }
1458 if err := os.WriteFile(filepath.Join(evalTmpDir, "a", "file"), nil, 0666); err != nil {
1459 t.Fatal(err)
1460 }
1461
1462
1463 vol := filepath.VolumeName(evalTmpDir)
1464 c := strings.Count(evalTmpDir[len(vol):], string(os.PathSeparator))
1465 var dd []string
1466 for i := 0; i < c+2; i++ {
1467 dd = append(dd, "..")
1468 }
1469
1470 wantSuffix := strings.Join([]string{"a", "file"}, string(os.PathSeparator))
1471
1472
1473 for _, i := range []int{c, c + 1, c + 2} {
1474 check := strings.Join([]string{evalTmpDir, strings.Join(dd[:i], string(os.PathSeparator)), evalTmpDir[len(vol)+1:], "b", "file"}, string(os.PathSeparator))
1475 resolved, err := filepath.EvalSymlinks(check)
1476 switch {
1477 case runtime.GOOS == "darwin" && errors.Is(err, fs.ErrNotExist):
1478
1479 testenv.SkipFlaky(t, 37910)
1480 case err != nil:
1481 t.Errorf("EvalSymlinks(%q) failed: %v", check, err)
1482 case !strings.HasSuffix(resolved, wantSuffix):
1483 t.Errorf("EvalSymlinks(%q) = %q does not end with %q", check, resolved, wantSuffix)
1484 default:
1485 t.Logf("EvalSymlinks(%q) = %q", check, resolved)
1486 }
1487 }
1488 }
1489
1490
1491 func TestEvalSymlinksAboveRootChdir(t *testing.T) {
1492 testenv.MustHaveSymlink(t)
1493
1494 tmpDir, err := os.MkdirTemp("", "TestEvalSymlinksAboveRootChdir")
1495 if err != nil {
1496 t.Fatal(err)
1497 }
1498 defer os.RemoveAll(tmpDir)
1499 chdir(t, tmpDir)
1500
1501 subdir := filepath.Join("a", "b")
1502 if err := os.MkdirAll(subdir, 0777); err != nil {
1503 t.Fatal(err)
1504 }
1505 if err := os.Symlink(subdir, "c"); err != nil {
1506 t.Fatal(err)
1507 }
1508 if err := os.WriteFile(filepath.Join(subdir, "file"), nil, 0666); err != nil {
1509 t.Fatal(err)
1510 }
1511
1512 subdir = filepath.Join("d", "e", "f")
1513 if err := os.MkdirAll(subdir, 0777); err != nil {
1514 t.Fatal(err)
1515 }
1516 if err := os.Chdir(subdir); err != nil {
1517 t.Fatal(err)
1518 }
1519
1520 check := filepath.Join("..", "..", "..", "c", "file")
1521 wantSuffix := filepath.Join("a", "b", "file")
1522 if resolved, err := filepath.EvalSymlinks(check); err != nil {
1523 t.Errorf("EvalSymlinks(%q) failed: %v", check, err)
1524 } else if !strings.HasSuffix(resolved, wantSuffix) {
1525 t.Errorf("EvalSymlinks(%q) = %q does not end with %q", check, resolved, wantSuffix)
1526 } else {
1527 t.Logf("EvalSymlinks(%q) = %q", check, resolved)
1528 }
1529 }
1530
View as plain text