github.com/aloncn/graphics-go@v0.0.1/src/path/filepath/path_test.go (about) 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. 4 5 package filepath_test 6 7 import ( 8 "errors" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "reflect" 13 "runtime" 14 "strings" 15 "testing" 16 ) 17 18 var supportsSymlinks = true 19 20 type PathTest struct { 21 path, result string 22 } 23 24 var cleantests = []PathTest{ 25 // Already clean 26 {"abc", "abc"}, 27 {"abc/def", "abc/def"}, 28 {"a/b/c", "a/b/c"}, 29 {".", "."}, 30 {"..", ".."}, 31 {"../..", "../.."}, 32 {"../../abc", "../../abc"}, 33 {"/abc", "/abc"}, 34 {"/", "/"}, 35 36 // Empty is current dir 37 {"", "."}, 38 39 // Remove trailing slash 40 {"abc/", "abc"}, 41 {"abc/def/", "abc/def"}, 42 {"a/b/c/", "a/b/c"}, 43 {"./", "."}, 44 {"../", ".."}, 45 {"../../", "../.."}, 46 {"/abc/", "/abc"}, 47 48 // Remove doubled slash 49 {"abc//def//ghi", "abc/def/ghi"}, 50 {"//abc", "/abc"}, 51 {"///abc", "/abc"}, 52 {"//abc//", "/abc"}, 53 {"abc//", "abc"}, 54 55 // Remove . elements 56 {"abc/./def", "abc/def"}, 57 {"/./abc/def", "/abc/def"}, 58 {"abc/.", "abc"}, 59 60 // Remove .. elements 61 {"abc/def/ghi/../jkl", "abc/def/jkl"}, 62 {"abc/def/../ghi/../jkl", "abc/jkl"}, 63 {"abc/def/..", "abc"}, 64 {"abc/def/../..", "."}, 65 {"/abc/def/../..", "/"}, 66 {"abc/def/../../..", ".."}, 67 {"/abc/def/../../..", "/"}, 68 {"abc/def/../../../ghi/jkl/../../../mno", "../../mno"}, 69 {"/../abc", "/abc"}, 70 71 // Combinations 72 {"abc/./../def", "def"}, 73 {"abc//./../def", "def"}, 74 {"abc/../../././../def", "../../def"}, 75 } 76 77 var wincleantests = []PathTest{ 78 {`c:`, `c:.`}, 79 {`c:\`, `c:\`}, 80 {`c:\abc`, `c:\abc`}, 81 {`c:abc\..\..\.\.\..\def`, `c:..\..\def`}, 82 {`c:\abc\def\..\..`, `c:\`}, 83 {`c:\..\abc`, `c:\abc`}, 84 {`c:..\abc`, `c:..\abc`}, 85 {`\`, `\`}, 86 {`/`, `\`}, 87 {`\\i\..\c$`, `\c$`}, 88 {`\\i\..\i\c$`, `\i\c$`}, 89 {`\\i\..\I\c$`, `\I\c$`}, 90 {`\\host\share\foo\..\bar`, `\\host\share\bar`}, 91 {`//host/share/foo/../baz`, `\\host\share\baz`}, 92 {`\\a\b\..\c`, `\\a\b\c`}, 93 {`\\a\b`, `\\a\b`}, 94 } 95 96 func TestClean(t *testing.T) { 97 tests := cleantests 98 if runtime.GOOS == "windows" { 99 for i := range tests { 100 tests[i].result = filepath.FromSlash(tests[i].result) 101 } 102 tests = append(tests, wincleantests...) 103 } 104 for _, test := range tests { 105 if s := filepath.Clean(test.path); s != test.result { 106 t.Errorf("Clean(%q) = %q, want %q", test.path, s, test.result) 107 } 108 if s := filepath.Clean(test.result); s != test.result { 109 t.Errorf("Clean(%q) = %q, want %q", test.result, s, test.result) 110 } 111 } 112 113 if testing.Short() { 114 t.Skip("skipping malloc count in short mode") 115 } 116 if runtime.GOMAXPROCS(0) > 1 { 117 t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1") 118 return 119 } 120 121 for _, test := range tests { 122 allocs := testing.AllocsPerRun(100, func() { filepath.Clean(test.result) }) 123 if allocs > 0 { 124 t.Errorf("Clean(%q): %v allocs, want zero", test.result, allocs) 125 } 126 } 127 } 128 129 const sep = filepath.Separator 130 131 var slashtests = []PathTest{ 132 {"", ""}, 133 {"/", string(sep)}, 134 {"/a/b", string([]byte{sep, 'a', sep, 'b'})}, 135 {"a//b", string([]byte{'a', sep, sep, 'b'})}, 136 } 137 138 func TestFromAndToSlash(t *testing.T) { 139 for _, test := range slashtests { 140 if s := filepath.FromSlash(test.path); s != test.result { 141 t.Errorf("FromSlash(%q) = %q, want %q", test.path, s, test.result) 142 } 143 if s := filepath.ToSlash(test.result); s != test.path { 144 t.Errorf("ToSlash(%q) = %q, want %q", test.result, s, test.path) 145 } 146 } 147 } 148 149 type SplitListTest struct { 150 list string 151 result []string 152 } 153 154 const lsep = filepath.ListSeparator 155 156 var splitlisttests = []SplitListTest{ 157 {"", []string{}}, 158 {string([]byte{'a', lsep, 'b'}), []string{"a", "b"}}, 159 {string([]byte{lsep, 'a', lsep, 'b'}), []string{"", "a", "b"}}, 160 } 161 162 var winsplitlisttests = []SplitListTest{ 163 // quoted 164 {`"a"`, []string{`a`}}, 165 166 // semicolon 167 {`";"`, []string{`;`}}, 168 {`"a;b"`, []string{`a;b`}}, 169 {`";";`, []string{`;`, ``}}, 170 {`;";"`, []string{``, `;`}}, 171 172 // partially quoted 173 {`a";"b`, []string{`a;b`}}, 174 {`a; ""b`, []string{`a`, ` b`}}, 175 {`"a;b`, []string{`a;b`}}, 176 {`""a;b`, []string{`a`, `b`}}, 177 {`"""a;b`, []string{`a;b`}}, 178 {`""""a;b`, []string{`a`, `b`}}, 179 {`a";b`, []string{`a;b`}}, 180 {`a;b";c`, []string{`a`, `b;c`}}, 181 {`"a";b";c`, []string{`a`, `b;c`}}, 182 } 183 184 func TestSplitList(t *testing.T) { 185 tests := splitlisttests 186 if runtime.GOOS == "windows" { 187 tests = append(tests, winsplitlisttests...) 188 } 189 for _, test := range tests { 190 if l := filepath.SplitList(test.list); !reflect.DeepEqual(l, test.result) { 191 t.Errorf("SplitList(%#q) = %#q, want %#q", test.list, l, test.result) 192 } 193 } 194 } 195 196 type SplitTest struct { 197 path, dir, file string 198 } 199 200 var unixsplittests = []SplitTest{ 201 {"a/b", "a/", "b"}, 202 {"a/b/", "a/b/", ""}, 203 {"a/", "a/", ""}, 204 {"a", "", "a"}, 205 {"/", "/", ""}, 206 } 207 208 var winsplittests = []SplitTest{ 209 {`c:`, `c:`, ``}, 210 {`c:/`, `c:/`, ``}, 211 {`c:/foo`, `c:/`, `foo`}, 212 {`c:/foo/bar`, `c:/foo/`, `bar`}, 213 {`//host/share`, `//host/share`, ``}, 214 {`//host/share/`, `//host/share/`, ``}, 215 {`//host/share/foo`, `//host/share/`, `foo`}, 216 {`\\host\share`, `\\host\share`, ``}, 217 {`\\host\share\`, `\\host\share\`, ``}, 218 {`\\host\share\foo`, `\\host\share\`, `foo`}, 219 } 220 221 func TestSplit(t *testing.T) { 222 var splittests []SplitTest 223 splittests = unixsplittests 224 if runtime.GOOS == "windows" { 225 splittests = append(splittests, winsplittests...) 226 } 227 for _, test := range splittests { 228 if d, f := filepath.Split(test.path); d != test.dir || f != test.file { 229 t.Errorf("Split(%q) = %q, %q, want %q, %q", test.path, d, f, test.dir, test.file) 230 } 231 } 232 } 233 234 type JoinTest struct { 235 elem []string 236 path string 237 } 238 239 var jointests = []JoinTest{ 240 // zero parameters 241 {[]string{}, ""}, 242 243 // one parameter 244 {[]string{""}, ""}, 245 {[]string{"/"}, "/"}, 246 {[]string{"a"}, "a"}, 247 248 // two parameters 249 {[]string{"a", "b"}, "a/b"}, 250 {[]string{"a", ""}, "a"}, 251 {[]string{"", "b"}, "b"}, 252 {[]string{"/", "a"}, "/a"}, 253 {[]string{"/", "a/b"}, "/a/b"}, 254 {[]string{"/", ""}, "/"}, 255 {[]string{"//", "a"}, "/a"}, 256 {[]string{"/a", "b"}, "/a/b"}, 257 {[]string{"a/", "b"}, "a/b"}, 258 {[]string{"a/", ""}, "a"}, 259 {[]string{"", ""}, ""}, 260 261 // three parameters 262 {[]string{"/", "a", "b"}, "/a/b"}, 263 } 264 265 var winjointests = []JoinTest{ 266 {[]string{`directory`, `file`}, `directory\file`}, 267 {[]string{`C:\Windows\`, `System32`}, `C:\Windows\System32`}, 268 {[]string{`C:\Windows\`, ``}, `C:\Windows`}, 269 {[]string{`C:\`, `Windows`}, `C:\Windows`}, 270 {[]string{`C:`, `a`}, `C:a`}, 271 {[]string{`C:`, `a\b`}, `C:a\b`}, 272 {[]string{`C:`, `a`, `b`}, `C:a\b`}, 273 {[]string{`C:.`, `a`}, `C:a`}, 274 {[]string{`C:a`, `b`}, `C:a\b`}, 275 {[]string{`C:a`, `b`, `d`}, `C:a\b\d`}, 276 {[]string{`\\host\share`, `foo`}, `\\host\share\foo`}, 277 {[]string{`\\host\share\foo`}, `\\host\share\foo`}, 278 {[]string{`//host/share`, `foo/bar`}, `\\host\share\foo\bar`}, 279 {[]string{`\`}, `\`}, 280 {[]string{`\`, ``}, `\`}, 281 {[]string{`\`, `a`}, `\a`}, 282 {[]string{`\\`, `a`}, `\a`}, 283 {[]string{`\`, `a`, `b`}, `\a\b`}, 284 {[]string{`\\`, `a`, `b`}, `\a\b`}, 285 {[]string{`\`, `\\a\b`, `c`}, `\a\b\c`}, 286 {[]string{`\\a`, `b`, `c`}, `\a\b\c`}, 287 {[]string{`\\a\`, `b`, `c`}, `\a\b\c`}, 288 } 289 290 func TestJoin(t *testing.T) { 291 if runtime.GOOS == "windows" { 292 jointests = append(jointests, winjointests...) 293 } 294 for _, test := range jointests { 295 expected := filepath.FromSlash(test.path) 296 if p := filepath.Join(test.elem...); p != expected { 297 t.Errorf("join(%q) = %q, want %q", test.elem, p, expected) 298 } 299 } 300 } 301 302 type ExtTest struct { 303 path, ext string 304 } 305 306 var exttests = []ExtTest{ 307 {"path.go", ".go"}, 308 {"path.pb.go", ".go"}, 309 {"a.dir/b", ""}, 310 {"a.dir/b.go", ".go"}, 311 {"a.dir/", ""}, 312 } 313 314 func TestExt(t *testing.T) { 315 for _, test := range exttests { 316 if x := filepath.Ext(test.path); x != test.ext { 317 t.Errorf("Ext(%q) = %q, want %q", test.path, x, test.ext) 318 } 319 } 320 } 321 322 type Node struct { 323 name string 324 entries []*Node // nil if the entry is a file 325 mark int 326 } 327 328 var tree = &Node{ 329 "testdata", 330 []*Node{ 331 {"a", nil, 0}, 332 {"b", []*Node{}, 0}, 333 {"c", nil, 0}, 334 { 335 "d", 336 []*Node{ 337 {"x", nil, 0}, 338 {"y", []*Node{}, 0}, 339 { 340 "z", 341 []*Node{ 342 {"u", nil, 0}, 343 {"v", nil, 0}, 344 }, 345 0, 346 }, 347 }, 348 0, 349 }, 350 }, 351 0, 352 } 353 354 func walkTree(n *Node, path string, f func(path string, n *Node)) { 355 f(path, n) 356 for _, e := range n.entries { 357 walkTree(e, filepath.Join(path, e.name), f) 358 } 359 } 360 361 func makeTree(t *testing.T) { 362 walkTree(tree, tree.name, func(path string, n *Node) { 363 if n.entries == nil { 364 fd, err := os.Create(path) 365 if err != nil { 366 t.Errorf("makeTree: %v", err) 367 return 368 } 369 fd.Close() 370 } else { 371 os.Mkdir(path, 0770) 372 } 373 }) 374 } 375 376 func markTree(n *Node) { walkTree(n, "", func(path string, n *Node) { n.mark++ }) } 377 378 func checkMarks(t *testing.T, report bool) { 379 walkTree(tree, tree.name, func(path string, n *Node) { 380 if n.mark != 1 && report { 381 t.Errorf("node %s mark = %d; expected 1", path, n.mark) 382 } 383 n.mark = 0 384 }) 385 } 386 387 // Assumes that each node name is unique. Good enough for a test. 388 // If clear is true, any incoming error is cleared before return. The errors 389 // are always accumulated, though. 390 func mark(path string, info os.FileInfo, err error, errors *[]error, clear bool) error { 391 if err != nil { 392 *errors = append(*errors, err) 393 if clear { 394 return nil 395 } 396 return err 397 } 398 name := info.Name() 399 walkTree(tree, tree.name, func(path string, n *Node) { 400 if n.name == name { 401 n.mark++ 402 } 403 }) 404 return nil 405 } 406 407 func chtmpdir(t *testing.T) (restore func()) { 408 oldwd, err := os.Getwd() 409 if err != nil { 410 t.Fatalf("chtmpdir: %v", err) 411 } 412 d, err := ioutil.TempDir("", "test") 413 if err != nil { 414 t.Fatalf("chtmpdir: %v", err) 415 } 416 if err := os.Chdir(d); err != nil { 417 t.Fatalf("chtmpdir: %v", err) 418 } 419 return func() { 420 if err := os.Chdir(oldwd); err != nil { 421 t.Fatalf("chtmpdir: %v", err) 422 } 423 os.RemoveAll(d) 424 } 425 } 426 427 func TestWalk(t *testing.T) { 428 if runtime.GOOS == "darwin" { 429 switch runtime.GOARCH { 430 case "arm", "arm64": 431 restore := chtmpdir(t) 432 defer restore() 433 } 434 } 435 makeTree(t) 436 errors := make([]error, 0, 10) 437 clear := true 438 markFn := func(path string, info os.FileInfo, err error) error { 439 return mark(path, info, err, &errors, clear) 440 } 441 // Expect no errors. 442 err := filepath.Walk(tree.name, markFn) 443 if err != nil { 444 t.Fatalf("no error expected, found: %s", err) 445 } 446 if len(errors) != 0 { 447 t.Fatalf("unexpected errors: %s", errors) 448 } 449 checkMarks(t, true) 450 errors = errors[0:0] 451 452 // Test permission errors. Only possible if we're not root 453 // and only on some file systems (AFS, FAT). To avoid errors during 454 // all.bash on those file systems, skip during go test -short. 455 if os.Getuid() > 0 && !testing.Short() { 456 // introduce 2 errors: chmod top-level directories to 0 457 os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0) 458 os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0) 459 460 // 3) capture errors, expect two. 461 // mark respective subtrees manually 462 markTree(tree.entries[1]) 463 markTree(tree.entries[3]) 464 // correct double-marking of directory itself 465 tree.entries[1].mark-- 466 tree.entries[3].mark-- 467 err := filepath.Walk(tree.name, markFn) 468 if err != nil { 469 t.Fatalf("expected no error return from Walk, got %s", err) 470 } 471 if len(errors) != 2 { 472 t.Errorf("expected 2 errors, got %d: %s", len(errors), errors) 473 } 474 // the inaccessible subtrees were marked manually 475 checkMarks(t, true) 476 errors = errors[0:0] 477 478 // 4) capture errors, stop after first error. 479 // mark respective subtrees manually 480 markTree(tree.entries[1]) 481 markTree(tree.entries[3]) 482 // correct double-marking of directory itself 483 tree.entries[1].mark-- 484 tree.entries[3].mark-- 485 clear = false // error will stop processing 486 err = filepath.Walk(tree.name, markFn) 487 if err == nil { 488 t.Fatalf("expected error return from Walk") 489 } 490 if len(errors) != 1 { 491 t.Errorf("expected 1 error, got %d: %s", len(errors), errors) 492 } 493 // the inaccessible subtrees were marked manually 494 checkMarks(t, false) 495 errors = errors[0:0] 496 497 // restore permissions 498 os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770) 499 os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770) 500 } 501 502 // cleanup 503 if err := os.RemoveAll(tree.name); err != nil { 504 t.Errorf("removeTree: %v", err) 505 } 506 } 507 508 func touch(t *testing.T, name string) { 509 f, err := os.Create(name) 510 if err != nil { 511 t.Fatal(err) 512 } 513 if err := f.Close(); err != nil { 514 t.Fatal(err) 515 } 516 } 517 518 func TestWalkSkipDirOnFile(t *testing.T) { 519 td, err := ioutil.TempDir("", "walktest") 520 if err != nil { 521 t.Fatal(err) 522 } 523 defer os.RemoveAll(td) 524 525 if err := os.MkdirAll(filepath.Join(td, "dir"), 0755); err != nil { 526 t.Fatal(err) 527 } 528 touch(t, filepath.Join(td, "dir/foo1")) 529 touch(t, filepath.Join(td, "dir/foo2")) 530 531 sawFoo2 := false 532 filepath.Walk(td, func(path string, info os.FileInfo, err error) error { 533 if strings.HasSuffix(path, "foo2") { 534 sawFoo2 = true 535 } 536 if strings.HasSuffix(path, "foo1") { 537 return filepath.SkipDir 538 } 539 return nil 540 }) 541 542 if sawFoo2 { 543 t.Errorf("SkipDir on file foo1 did not block processing of foo2") 544 } 545 } 546 547 func TestWalkFileError(t *testing.T) { 548 td, err := ioutil.TempDir("", "walktest") 549 if err != nil { 550 t.Fatal(err) 551 } 552 defer os.RemoveAll(td) 553 554 touch(t, filepath.Join(td, "foo")) 555 touch(t, filepath.Join(td, "bar")) 556 dir := filepath.Join(td, "dir") 557 if err := os.MkdirAll(filepath.Join(td, "dir"), 0755); err != nil { 558 t.Fatal(err) 559 } 560 touch(t, filepath.Join(dir, "baz")) 561 touch(t, filepath.Join(dir, "stat-error")) 562 defer func() { 563 *filepath.LstatP = os.Lstat 564 }() 565 statErr := errors.New("some stat error") 566 *filepath.LstatP = func(path string) (os.FileInfo, error) { 567 if strings.HasSuffix(path, "stat-error") { 568 return nil, statErr 569 } 570 return os.Lstat(path) 571 } 572 got := map[string]error{} 573 err = filepath.Walk(td, func(path string, fi os.FileInfo, err error) error { 574 rel, _ := filepath.Rel(td, path) 575 got[filepath.ToSlash(rel)] = err 576 return nil 577 }) 578 if err != nil { 579 t.Errorf("Walk error: %v", err) 580 } 581 want := map[string]error{ 582 ".": nil, 583 "foo": nil, 584 "bar": nil, 585 "dir": nil, 586 "dir/baz": nil, 587 "dir/stat-error": statErr, 588 } 589 if !reflect.DeepEqual(got, want) { 590 t.Errorf("Walked %#v; want %#v", got, want) 591 } 592 } 593 594 var basetests = []PathTest{ 595 {"", "."}, 596 {".", "."}, 597 {"/.", "."}, 598 {"/", "/"}, 599 {"////", "/"}, 600 {"x/", "x"}, 601 {"abc", "abc"}, 602 {"abc/def", "def"}, 603 {"a/b/.x", ".x"}, 604 {"a/b/c.", "c."}, 605 {"a/b/c.x", "c.x"}, 606 } 607 608 var winbasetests = []PathTest{ 609 {`c:\`, `\`}, 610 {`c:.`, `.`}, 611 {`c:\a\b`, `b`}, 612 {`c:a\b`, `b`}, 613 {`c:a\b\c`, `c`}, 614 {`\\host\share\`, `\`}, 615 {`\\host\share\a`, `a`}, 616 {`\\host\share\a\b`, `b`}, 617 } 618 619 func TestBase(t *testing.T) { 620 tests := basetests 621 if runtime.GOOS == "windows" { 622 // make unix tests work on windows 623 for i := range tests { 624 tests[i].result = filepath.Clean(tests[i].result) 625 } 626 // add windows specific tests 627 tests = append(tests, winbasetests...) 628 } 629 for _, test := range tests { 630 if s := filepath.Base(test.path); s != test.result { 631 t.Errorf("Base(%q) = %q, want %q", test.path, s, test.result) 632 } 633 } 634 } 635 636 var dirtests = []PathTest{ 637 {"", "."}, 638 {".", "."}, 639 {"/.", "/"}, 640 {"/", "/"}, 641 {"////", "/"}, 642 {"/foo", "/"}, 643 {"x/", "x"}, 644 {"abc", "."}, 645 {"abc/def", "abc"}, 646 {"a/b/.x", "a/b"}, 647 {"a/b/c.", "a/b"}, 648 {"a/b/c.x", "a/b"}, 649 } 650 651 var windirtests = []PathTest{ 652 {`c:\`, `c:\`}, 653 {`c:.`, `c:.`}, 654 {`c:\a\b`, `c:\a`}, 655 {`c:a\b`, `c:a`}, 656 {`c:a\b\c`, `c:a\b`}, 657 {`\\host\share\`, `\\host\share\`}, 658 {`\\host\share\a`, `\\host\share\`}, 659 {`\\host\share\a\b`, `\\host\share\a`}, 660 } 661 662 func TestDir(t *testing.T) { 663 tests := dirtests 664 if runtime.GOOS == "windows" { 665 // make unix tests work on windows 666 for i := range tests { 667 tests[i].result = filepath.Clean(tests[i].result) 668 } 669 // add windows specific tests 670 tests = append(tests, windirtests...) 671 } 672 for _, test := range tests { 673 if s := filepath.Dir(test.path); s != test.result { 674 t.Errorf("Dir(%q) = %q, want %q", test.path, s, test.result) 675 } 676 } 677 } 678 679 type IsAbsTest struct { 680 path string 681 isAbs bool 682 } 683 684 var isabstests = []IsAbsTest{ 685 {"", false}, 686 {"/", true}, 687 {"/usr/bin/gcc", true}, 688 {"..", false}, 689 {"/a/../bb", true}, 690 {".", false}, 691 {"./", false}, 692 {"lala", false}, 693 } 694 695 var winisabstests = []IsAbsTest{ 696 {`C:\`, true}, 697 {`c\`, false}, 698 {`c::`, false}, 699 {`c:`, false}, 700 {`/`, false}, 701 {`\`, false}, 702 {`\Windows`, false}, 703 {`c:a\b`, false}, 704 {`c:\a\b`, true}, 705 {`c:/a/b`, true}, 706 {`\\host\share\foo`, true}, 707 {`//host/share/foo/bar`, true}, 708 } 709 710 func TestIsAbs(t *testing.T) { 711 var tests []IsAbsTest 712 if runtime.GOOS == "windows" { 713 tests = append(tests, winisabstests...) 714 // All non-windows tests should fail, because they have no volume letter. 715 for _, test := range isabstests { 716 tests = append(tests, IsAbsTest{test.path, false}) 717 } 718 // All non-windows test should work as intended if prefixed with volume letter. 719 for _, test := range isabstests { 720 tests = append(tests, IsAbsTest{"c:" + test.path, test.isAbs}) 721 } 722 } else { 723 tests = isabstests 724 } 725 726 for _, test := range tests { 727 if r := filepath.IsAbs(test.path); r != test.isAbs { 728 t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs) 729 } 730 } 731 } 732 733 type EvalSymlinksTest struct { 734 // If dest is empty, the path is created; otherwise the dest is symlinked to the path. 735 path, dest string 736 } 737 738 var EvalSymlinksTestDirs = []EvalSymlinksTest{ 739 {"test", ""}, 740 {"test/dir", ""}, 741 {"test/dir/link3", "../../"}, 742 {"test/link1", "../test"}, 743 {"test/link2", "dir"}, 744 {"test/linkabs", "/"}, 745 } 746 747 var EvalSymlinksTests = []EvalSymlinksTest{ 748 {"test", "test"}, 749 {"test/dir", "test/dir"}, 750 {"test/dir/../..", "."}, 751 {"test/link1", "test"}, 752 {"test/link2", "test/dir"}, 753 {"test/link1/dir", "test/dir"}, 754 {"test/link2/..", "test"}, 755 {"test/dir/link3", "."}, 756 {"test/link2/link3/test", "test"}, 757 {"test/linkabs", "/"}, 758 } 759 760 // findEvalSymlinksTestDirsDest searches testDirs 761 // for matching path and returns correspondent dest. 762 func findEvalSymlinksTestDirsDest(t *testing.T, testDirs []EvalSymlinksTest, path string) string { 763 for _, d := range testDirs { 764 if d.path == path { 765 return d.dest 766 } 767 } 768 t.Fatalf("did not find %q in testDirs slice", path) 769 return "" 770 } 771 772 // simpleJoin builds a file name from the directory and path. 773 // It does not use Join because we don't want ".." to be evaluated. 774 func simpleJoin(dir, path string) string { 775 return dir + string(filepath.Separator) + path 776 } 777 778 func TestEvalSymlinks(t *testing.T) { 779 switch runtime.GOOS { 780 case "android", "nacl", "plan9": 781 t.Skipf("skipping on %s", runtime.GOOS) 782 } 783 if !supportsSymlinks { 784 t.Skip("skipping because symlinks are not supported") 785 } 786 787 tmpDir, err := ioutil.TempDir("", "evalsymlink") 788 if err != nil { 789 t.Fatal("creating temp dir:", err) 790 } 791 defer os.RemoveAll(tmpDir) 792 793 // /tmp may itself be a symlink! Avoid the confusion, although 794 // it means trusting the thing we're testing. 795 tmpDir, err = filepath.EvalSymlinks(tmpDir) 796 if err != nil { 797 t.Fatal("eval symlink for tmp dir:", err) 798 } 799 800 tests := EvalSymlinksTests 801 testdirs := EvalSymlinksTestDirs 802 if runtime.GOOS == "windows" { 803 if len(tmpDir) < 3 { 804 t.Fatalf("tmpDir path %q is too short", tmpDir) 805 } 806 if tmpDir[1] != ':' { 807 t.Fatalf("tmpDir path %q must have drive letter in it", tmpDir) 808 } 809 newtest := EvalSymlinksTest{"test/linkabswin", tmpDir[:3]} 810 tests = append(tests, newtest) 811 testdirs = append(testdirs, newtest) 812 } 813 814 // Create the symlink farm using relative paths. 815 for _, d := range testdirs { 816 var err error 817 path := simpleJoin(tmpDir, d.path) 818 if d.dest == "" { 819 err = os.Mkdir(path, 0755) 820 } else { 821 err = os.Symlink(d.dest, path) 822 } 823 if err != nil { 824 t.Fatal(err) 825 } 826 } 827 828 wd, err := os.Getwd() 829 if err != nil { 830 t.Fatal(err) 831 } 832 833 // Evaluate the symlink farm. 834 for _, d := range tests { 835 path := simpleJoin(tmpDir, d.path) 836 dest := simpleJoin(tmpDir, d.dest) 837 if filepath.IsAbs(d.dest) || os.IsPathSeparator(d.dest[0]) { 838 dest = d.dest 839 } 840 if p, err := filepath.EvalSymlinks(path); err != nil { 841 t.Errorf("EvalSymlinks(%q) error: %v", d.path, err) 842 } else if filepath.Clean(p) != filepath.Clean(dest) { 843 t.Errorf("Clean(%q)=%q, want %q", path, p, dest) 844 } 845 846 // test EvalSymlinks(".") 847 func() { 848 defer func() { 849 err := os.Chdir(wd) 850 if err != nil { 851 t.Fatal(err) 852 } 853 }() 854 855 err := os.Chdir(path) 856 if err != nil { 857 t.Error(err) 858 return 859 } 860 p, err := filepath.EvalSymlinks(".") 861 if err != nil { 862 t.Errorf(`EvalSymlinks(".") in %q directory error: %v`, d.path, err) 863 return 864 } 865 if p == "." { 866 return 867 } 868 want := filepath.Clean(findEvalSymlinksTestDirsDest(t, testdirs, d.path)) 869 if p == want { 870 return 871 } 872 t.Errorf(`EvalSymlinks(".") in %q directory returns %q, want "." or %q`, d.path, p, want) 873 }() 874 875 // test EvalSymlinks where parameter is relative path 876 func() { 877 defer func() { 878 err := os.Chdir(wd) 879 if err != nil { 880 t.Fatal(err) 881 } 882 }() 883 884 err := os.Chdir(tmpDir) 885 if err != nil { 886 t.Error(err) 887 return 888 } 889 if p, err := filepath.EvalSymlinks(d.path); err != nil { 890 t.Errorf("EvalSymlinks(%q) error: %v", d.path, err) 891 } else if filepath.Clean(p) != filepath.Clean(d.dest) { 892 t.Errorf("Clean(%q)=%q, want %q", d.path, p, d.dest) 893 } 894 }() 895 } 896 } 897 898 func TestIssue13582(t *testing.T) { 899 switch runtime.GOOS { 900 case "android", "nacl", "plan9": 901 t.Skipf("skipping on %s", runtime.GOOS) 902 } 903 if !supportsSymlinks { 904 t.Skip("skipping because symlinks are not supported") 905 } 906 907 tmpDir, err := ioutil.TempDir("", "issue13582") 908 if err != nil { 909 t.Fatal(err) 910 } 911 defer os.RemoveAll(tmpDir) 912 913 dir := filepath.Join(tmpDir, "dir") 914 err = os.Mkdir(dir, 0755) 915 if err != nil { 916 t.Fatal(err) 917 } 918 linkToDir := filepath.Join(tmpDir, "link_to_dir") 919 err = os.Symlink(dir, linkToDir) 920 if err != nil { 921 t.Fatal(err) 922 } 923 file := filepath.Join(linkToDir, "file") 924 err = ioutil.WriteFile(file, nil, 0644) 925 if err != nil { 926 t.Fatal(err) 927 } 928 link1 := filepath.Join(linkToDir, "link1") 929 err = os.Symlink(file, link1) 930 if err != nil { 931 t.Fatal(err) 932 } 933 link2 := filepath.Join(linkToDir, "link2") 934 err = os.Symlink(link1, link2) 935 if err != nil { 936 t.Fatal(err) 937 } 938 939 // /tmp may itself be a symlink! 940 realTmpDir, err := filepath.EvalSymlinks(tmpDir) 941 if err != nil { 942 t.Fatal(err) 943 } 944 realDir := filepath.Join(realTmpDir, "dir") 945 realFile := filepath.Join(realDir, "file") 946 947 tests := []struct { 948 path, want string 949 }{ 950 {dir, realDir}, 951 {linkToDir, realDir}, 952 {file, realFile}, 953 {link1, realFile}, 954 {link2, realFile}, 955 } 956 for i, test := range tests { 957 have, err := filepath.EvalSymlinks(test.path) 958 if err != nil { 959 t.Fatal(err) 960 } 961 if have != test.want { 962 t.Errorf("test#%d: EvalSymlinks(%q) returns %q, want %q", i, test.path, have, test.want) 963 } 964 } 965 } 966 967 // Test directories relative to temporary directory. 968 // The tests are run in absTestDirs[0]. 969 var absTestDirs = []string{ 970 "a", 971 "a/b", 972 "a/b/c", 973 } 974 975 // Test paths relative to temporary directory. $ expands to the directory. 976 // The tests are run in absTestDirs[0]. 977 // We create absTestDirs first. 978 var absTests = []string{ 979 ".", 980 "b", 981 "../a", 982 "../a/b", 983 "../a/b/./c/../../.././a", 984 "$", 985 "$/.", 986 "$/a/../a/b", 987 "$/a/b/c/../../.././a", 988 } 989 990 func TestAbs(t *testing.T) { 991 root, err := ioutil.TempDir("", "TestAbs") 992 if err != nil { 993 t.Fatal("TempDir failed: ", err) 994 } 995 defer os.RemoveAll(root) 996 997 wd, err := os.Getwd() 998 if err != nil { 999 t.Fatal("getwd failed: ", err) 1000 } 1001 err = os.Chdir(root) 1002 if err != nil { 1003 t.Fatal("chdir failed: ", err) 1004 } 1005 defer os.Chdir(wd) 1006 1007 for _, dir := range absTestDirs { 1008 err = os.Mkdir(dir, 0777) 1009 if err != nil { 1010 t.Fatal("Mkdir failed: ", err) 1011 } 1012 } 1013 1014 if runtime.GOOS == "windows" { 1015 vol := filepath.VolumeName(root) 1016 var extra []string 1017 for _, path := range absTests { 1018 if strings.Index(path, "$") != -1 { 1019 continue 1020 } 1021 path = vol + path 1022 extra = append(extra, path) 1023 } 1024 absTests = append(absTests, extra...) 1025 } 1026 1027 err = os.Chdir(absTestDirs[0]) 1028 if err != nil { 1029 t.Fatal("chdir failed: ", err) 1030 } 1031 1032 for _, path := range absTests { 1033 path = strings.Replace(path, "$", root, -1) 1034 info, err := os.Stat(path) 1035 if err != nil { 1036 t.Errorf("%s: %s", path, err) 1037 continue 1038 } 1039 1040 abspath, err := filepath.Abs(path) 1041 if err != nil { 1042 t.Errorf("Abs(%q) error: %v", path, err) 1043 continue 1044 } 1045 absinfo, err := os.Stat(abspath) 1046 if err != nil || !os.SameFile(absinfo, info) { 1047 t.Errorf("Abs(%q)=%q, not the same file", path, abspath) 1048 } 1049 if !filepath.IsAbs(abspath) { 1050 t.Errorf("Abs(%q)=%q, not an absolute path", path, abspath) 1051 } 1052 if filepath.IsAbs(path) && abspath != filepath.Clean(path) { 1053 t.Errorf("Abs(%q)=%q, isn't clean", path, abspath) 1054 } 1055 } 1056 } 1057 1058 type RelTests struct { 1059 root, path, want string 1060 } 1061 1062 var reltests = []RelTests{ 1063 {"a/b", "a/b", "."}, 1064 {"a/b/.", "a/b", "."}, 1065 {"a/b", "a/b/.", "."}, 1066 {"./a/b", "a/b", "."}, 1067 {"a/b", "./a/b", "."}, 1068 {"ab/cd", "ab/cde", "../cde"}, 1069 {"ab/cd", "ab/c", "../c"}, 1070 {"a/b", "a/b/c/d", "c/d"}, 1071 {"a/b", "a/b/../c", "../c"}, 1072 {"a/b/../c", "a/b", "../b"}, 1073 {"a/b/c", "a/c/d", "../../c/d"}, 1074 {"a/b", "c/d", "../../c/d"}, 1075 {"a/b/c/d", "a/b", "../.."}, 1076 {"a/b/c/d", "a/b/", "../.."}, 1077 {"a/b/c/d/", "a/b", "../.."}, 1078 {"a/b/c/d/", "a/b/", "../.."}, 1079 {"../../a/b", "../../a/b/c/d", "c/d"}, 1080 {"/a/b", "/a/b", "."}, 1081 {"/a/b/.", "/a/b", "."}, 1082 {"/a/b", "/a/b/.", "."}, 1083 {"/ab/cd", "/ab/cde", "../cde"}, 1084 {"/ab/cd", "/ab/c", "../c"}, 1085 {"/a/b", "/a/b/c/d", "c/d"}, 1086 {"/a/b", "/a/b/../c", "../c"}, 1087 {"/a/b/../c", "/a/b", "../b"}, 1088 {"/a/b/c", "/a/c/d", "../../c/d"}, 1089 {"/a/b", "/c/d", "../../c/d"}, 1090 {"/a/b/c/d", "/a/b", "../.."}, 1091 {"/a/b/c/d", "/a/b/", "../.."}, 1092 {"/a/b/c/d/", "/a/b", "../.."}, 1093 {"/a/b/c/d/", "/a/b/", "../.."}, 1094 {"/../../a/b", "/../../a/b/c/d", "c/d"}, 1095 {".", "a/b", "a/b"}, 1096 {".", "..", ".."}, 1097 1098 // can't do purely lexically 1099 {"..", ".", "err"}, 1100 {"..", "a", "err"}, 1101 {"../..", "..", "err"}, 1102 {"a", "/a", "err"}, 1103 {"/a", "a", "err"}, 1104 } 1105 1106 var winreltests = []RelTests{ 1107 {`C:a\b\c`, `C:a/b/d`, `..\d`}, 1108 {`C:\`, `D:\`, `err`}, 1109 {`C:`, `D:`, `err`}, 1110 {`C:\Projects`, `c:\projects\src`, `src`}, 1111 {`C:\Projects`, `c:\projects`, `.`}, 1112 {`C:\Projects\a\..`, `c:\projects`, `.`}, 1113 } 1114 1115 func TestRel(t *testing.T) { 1116 tests := append([]RelTests{}, reltests...) 1117 if runtime.GOOS == "windows" { 1118 for i := range tests { 1119 tests[i].want = filepath.FromSlash(tests[i].want) 1120 } 1121 tests = append(tests, winreltests...) 1122 } 1123 for _, test := range tests { 1124 got, err := filepath.Rel(test.root, test.path) 1125 if test.want == "err" { 1126 if err == nil { 1127 t.Errorf("Rel(%q, %q)=%q, want error", test.root, test.path, got) 1128 } 1129 continue 1130 } 1131 if err != nil { 1132 t.Errorf("Rel(%q, %q): want %q, got error: %s", test.root, test.path, test.want, err) 1133 } 1134 if got != test.want { 1135 t.Errorf("Rel(%q, %q)=%q, want %q", test.root, test.path, got, test.want) 1136 } 1137 } 1138 } 1139 1140 type VolumeNameTest struct { 1141 path string 1142 vol string 1143 } 1144 1145 var volumenametests = []VolumeNameTest{ 1146 {`c:/foo/bar`, `c:`}, 1147 {`c:`, `c:`}, 1148 {`2:`, ``}, 1149 {``, ``}, 1150 {`\\\host`, ``}, 1151 {`\\\host\`, ``}, 1152 {`\\\host\share`, ``}, 1153 {`\\\host\\share`, ``}, 1154 {`\\host`, ``}, 1155 {`//host`, ``}, 1156 {`\\host\`, ``}, 1157 {`//host/`, ``}, 1158 {`\\host\share`, `\\host\share`}, 1159 {`//host/share`, `//host/share`}, 1160 {`\\host\share\`, `\\host\share`}, 1161 {`//host/share/`, `//host/share`}, 1162 {`\\host\share\foo`, `\\host\share`}, 1163 {`//host/share/foo`, `//host/share`}, 1164 {`\\host\share\\foo\\\bar\\\\baz`, `\\host\share`}, 1165 {`//host/share//foo///bar////baz`, `//host/share`}, 1166 {`\\host\share\foo\..\bar`, `\\host\share`}, 1167 {`//host/share/foo/../bar`, `//host/share`}, 1168 } 1169 1170 func TestVolumeName(t *testing.T) { 1171 if runtime.GOOS != "windows" { 1172 return 1173 } 1174 for _, v := range volumenametests { 1175 if vol := filepath.VolumeName(v.path); vol != v.vol { 1176 t.Errorf("VolumeName(%q)=%q, want %q", v.path, vol, v.vol) 1177 } 1178 } 1179 } 1180 1181 func TestDriveLetterInEvalSymlinks(t *testing.T) { 1182 if runtime.GOOS != "windows" { 1183 return 1184 } 1185 wd, _ := os.Getwd() 1186 if len(wd) < 3 { 1187 t.Errorf("Current directory path %q is too short", wd) 1188 } 1189 lp := strings.ToLower(wd) 1190 up := strings.ToUpper(wd) 1191 flp, err := filepath.EvalSymlinks(lp) 1192 if err != nil { 1193 t.Fatalf("EvalSymlinks(%q) failed: %q", lp, err) 1194 } 1195 fup, err := filepath.EvalSymlinks(up) 1196 if err != nil { 1197 t.Fatalf("EvalSymlinks(%q) failed: %q", up, err) 1198 } 1199 if flp != fup { 1200 t.Errorf("Results of EvalSymlinks do not match: %q and %q", flp, fup) 1201 } 1202 } 1203 1204 func TestBug3486(t *testing.T) { // https://golang.org/issue/3486 1205 if runtime.GOOS == "darwin" { 1206 switch runtime.GOARCH { 1207 case "arm", "arm64": 1208 t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH) 1209 } 1210 } 1211 root, err := filepath.EvalSymlinks(runtime.GOROOT() + "/test") 1212 if err != nil { 1213 t.Fatal(err) 1214 } 1215 bugs := filepath.Join(root, "bugs") 1216 ken := filepath.Join(root, "ken") 1217 seenBugs := false 1218 seenKen := false 1219 filepath.Walk(root, func(pth string, info os.FileInfo, err error) error { 1220 if err != nil { 1221 t.Fatal(err) 1222 } 1223 1224 switch pth { 1225 case bugs: 1226 seenBugs = true 1227 return filepath.SkipDir 1228 case ken: 1229 if !seenBugs { 1230 t.Fatal("filepath.Walk out of order - ken before bugs") 1231 } 1232 seenKen = true 1233 } 1234 return nil 1235 }) 1236 if !seenKen { 1237 t.Fatalf("%q not seen", ken) 1238 } 1239 }