rsc.io/go@v0.0.0-20150416155037-e040fd465409/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:`, `Windows`}, `C:\Windows`}, 271 {[]string{`\\host\share`, `foo`}, `\\host\share\foo`}, 272 {[]string{`\\host\share\foo`}, `\\host\share\foo`}, 273 {[]string{`//host/share`, `foo/bar`}, `\\host\share\foo\bar`}, 274 {[]string{`\`}, `\`}, 275 {[]string{`\`, ``}, `\`}, 276 {[]string{`\`, `a`}, `\a`}, 277 {[]string{`\\`, `a`}, `\a`}, 278 {[]string{`\`, `a`, `b`}, `\a\b`}, 279 {[]string{`\\`, `a`, `b`}, `\a\b`}, 280 {[]string{`\`, `\\a\b`, `c`}, `\a\b\c`}, 281 {[]string{`\\a`, `b`, `c`}, `\a\b\c`}, 282 {[]string{`\\a\`, `b`, `c`}, `\a\b\c`}, 283 } 284 285 func TestJoin(t *testing.T) { 286 if runtime.GOOS == "windows" { 287 jointests = append(jointests, winjointests...) 288 } 289 for _, test := range jointests { 290 expected := filepath.FromSlash(test.path) 291 if p := filepath.Join(test.elem...); p != expected { 292 t.Errorf("join(%q) = %q, want %q", test.elem, p, expected) 293 } 294 } 295 } 296 297 type ExtTest struct { 298 path, ext string 299 } 300 301 var exttests = []ExtTest{ 302 {"path.go", ".go"}, 303 {"path.pb.go", ".go"}, 304 {"a.dir/b", ""}, 305 {"a.dir/b.go", ".go"}, 306 {"a.dir/", ""}, 307 } 308 309 func TestExt(t *testing.T) { 310 for _, test := range exttests { 311 if x := filepath.Ext(test.path); x != test.ext { 312 t.Errorf("Ext(%q) = %q, want %q", test.path, x, test.ext) 313 } 314 } 315 } 316 317 type Node struct { 318 name string 319 entries []*Node // nil if the entry is a file 320 mark int 321 } 322 323 var tree = &Node{ 324 "testdata", 325 []*Node{ 326 {"a", nil, 0}, 327 {"b", []*Node{}, 0}, 328 {"c", nil, 0}, 329 { 330 "d", 331 []*Node{ 332 {"x", nil, 0}, 333 {"y", []*Node{}, 0}, 334 { 335 "z", 336 []*Node{ 337 {"u", nil, 0}, 338 {"v", nil, 0}, 339 }, 340 0, 341 }, 342 }, 343 0, 344 }, 345 }, 346 0, 347 } 348 349 func walkTree(n *Node, path string, f func(path string, n *Node)) { 350 f(path, n) 351 for _, e := range n.entries { 352 walkTree(e, filepath.Join(path, e.name), f) 353 } 354 } 355 356 func makeTree(t *testing.T) { 357 walkTree(tree, tree.name, func(path string, n *Node) { 358 if n.entries == nil { 359 fd, err := os.Create(path) 360 if err != nil { 361 t.Errorf("makeTree: %v", err) 362 return 363 } 364 fd.Close() 365 } else { 366 os.Mkdir(path, 0770) 367 } 368 }) 369 } 370 371 func markTree(n *Node) { walkTree(n, "", func(path string, n *Node) { n.mark++ }) } 372 373 func checkMarks(t *testing.T, report bool) { 374 walkTree(tree, tree.name, func(path string, n *Node) { 375 if n.mark != 1 && report { 376 t.Errorf("node %s mark = %d; expected 1", path, n.mark) 377 } 378 n.mark = 0 379 }) 380 } 381 382 // Assumes that each node name is unique. Good enough for a test. 383 // If clear is true, any incoming error is cleared before return. The errors 384 // are always accumulated, though. 385 func mark(path string, info os.FileInfo, err error, errors *[]error, clear bool) error { 386 if err != nil { 387 *errors = append(*errors, err) 388 if clear { 389 return nil 390 } 391 return err 392 } 393 name := info.Name() 394 walkTree(tree, tree.name, func(path string, n *Node) { 395 if n.name == name { 396 n.mark++ 397 } 398 }) 399 return nil 400 } 401 402 func chtmpdir(t *testing.T) (restore func()) { 403 oldwd, err := os.Getwd() 404 if err != nil { 405 t.Fatal("chtmpdir: %v", err) 406 } 407 d, err := ioutil.TempDir("", "test") 408 if err != nil { 409 t.Fatal("chtmpdir: %v", err) 410 } 411 if err := os.Chdir(d); err != nil { 412 t.Fatal("chtmpdir: %v", err) 413 } 414 return func() { 415 if err := os.Chdir(oldwd); err != nil { 416 t.Fatal("chtmpdir: %v", err) 417 } 418 os.RemoveAll(d) 419 } 420 } 421 422 func TestWalk(t *testing.T) { 423 if runtime.GOOS == "darwin" { 424 switch runtime.GOARCH { 425 case "arm", "arm64": 426 restore := chtmpdir(t) 427 defer restore() 428 } 429 } 430 makeTree(t) 431 errors := make([]error, 0, 10) 432 clear := true 433 markFn := func(path string, info os.FileInfo, err error) error { 434 return mark(path, info, err, &errors, clear) 435 } 436 // Expect no errors. 437 err := filepath.Walk(tree.name, markFn) 438 if err != nil { 439 t.Fatalf("no error expected, found: %s", err) 440 } 441 if len(errors) != 0 { 442 t.Fatalf("unexpected errors: %s", errors) 443 } 444 checkMarks(t, true) 445 errors = errors[0:0] 446 447 // Test permission errors. Only possible if we're not root 448 // and only on some file systems (AFS, FAT). To avoid errors during 449 // all.bash on those file systems, skip during go test -short. 450 if os.Getuid() > 0 && !testing.Short() { 451 // introduce 2 errors: chmod top-level directories to 0 452 os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0) 453 os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0) 454 455 // 3) capture errors, expect two. 456 // mark respective subtrees manually 457 markTree(tree.entries[1]) 458 markTree(tree.entries[3]) 459 // correct double-marking of directory itself 460 tree.entries[1].mark-- 461 tree.entries[3].mark-- 462 err := filepath.Walk(tree.name, markFn) 463 if err != nil { 464 t.Fatalf("expected no error return from Walk, got %s", err) 465 } 466 if len(errors) != 2 { 467 t.Errorf("expected 2 errors, got %d: %s", len(errors), errors) 468 } 469 // the inaccessible subtrees were marked manually 470 checkMarks(t, true) 471 errors = errors[0:0] 472 473 // 4) capture errors, stop after first error. 474 // mark respective subtrees manually 475 markTree(tree.entries[1]) 476 markTree(tree.entries[3]) 477 // correct double-marking of directory itself 478 tree.entries[1].mark-- 479 tree.entries[3].mark-- 480 clear = false // error will stop processing 481 err = filepath.Walk(tree.name, markFn) 482 if err == nil { 483 t.Fatalf("expected error return from Walk") 484 } 485 if len(errors) != 1 { 486 t.Errorf("expected 1 error, got %d: %s", len(errors), errors) 487 } 488 // the inaccessible subtrees were marked manually 489 checkMarks(t, false) 490 errors = errors[0:0] 491 492 // restore permissions 493 os.Chmod(filepath.Join(tree.name, tree.entries[1].name), 0770) 494 os.Chmod(filepath.Join(tree.name, tree.entries[3].name), 0770) 495 } 496 497 // cleanup 498 if err := os.RemoveAll(tree.name); err != nil { 499 t.Errorf("removeTree: %v", err) 500 } 501 } 502 503 func touch(t *testing.T, name string) { 504 f, err := os.Create(name) 505 if err != nil { 506 t.Fatal(err) 507 } 508 if err := f.Close(); err != nil { 509 t.Fatal(err) 510 } 511 } 512 513 func TestWalkFileError(t *testing.T) { 514 td, err := ioutil.TempDir("", "walktest") 515 if err != nil { 516 t.Fatal(err) 517 } 518 defer os.RemoveAll(td) 519 520 touch(t, filepath.Join(td, "foo")) 521 touch(t, filepath.Join(td, "bar")) 522 dir := filepath.Join(td, "dir") 523 if err := os.MkdirAll(filepath.Join(td, "dir"), 0755); err != nil { 524 t.Fatal(err) 525 } 526 touch(t, filepath.Join(dir, "baz")) 527 touch(t, filepath.Join(dir, "stat-error")) 528 defer func() { 529 *filepath.LstatP = os.Lstat 530 }() 531 statErr := errors.New("some stat error") 532 *filepath.LstatP = func(path string) (os.FileInfo, error) { 533 if strings.HasSuffix(path, "stat-error") { 534 return nil, statErr 535 } 536 return os.Lstat(path) 537 } 538 got := map[string]error{} 539 err = filepath.Walk(td, func(path string, fi os.FileInfo, err error) error { 540 rel, _ := filepath.Rel(td, path) 541 got[filepath.ToSlash(rel)] = err 542 return nil 543 }) 544 if err != nil { 545 t.Errorf("Walk error: %v", err) 546 } 547 want := map[string]error{ 548 ".": nil, 549 "foo": nil, 550 "bar": nil, 551 "dir": nil, 552 "dir/baz": nil, 553 "dir/stat-error": statErr, 554 } 555 if !reflect.DeepEqual(got, want) { 556 t.Errorf("Walked %#v; want %#v", got, want) 557 } 558 } 559 560 var basetests = []PathTest{ 561 {"", "."}, 562 {".", "."}, 563 {"/.", "."}, 564 {"/", "/"}, 565 {"////", "/"}, 566 {"x/", "x"}, 567 {"abc", "abc"}, 568 {"abc/def", "def"}, 569 {"a/b/.x", ".x"}, 570 {"a/b/c.", "c."}, 571 {"a/b/c.x", "c.x"}, 572 } 573 574 var winbasetests = []PathTest{ 575 {`c:\`, `\`}, 576 {`c:.`, `.`}, 577 {`c:\a\b`, `b`}, 578 {`c:a\b`, `b`}, 579 {`c:a\b\c`, `c`}, 580 {`\\host\share\`, `\`}, 581 {`\\host\share\a`, `a`}, 582 {`\\host\share\a\b`, `b`}, 583 } 584 585 func TestBase(t *testing.T) { 586 tests := basetests 587 if runtime.GOOS == "windows" { 588 // make unix tests work on windows 589 for i := range tests { 590 tests[i].result = filepath.Clean(tests[i].result) 591 } 592 // add windows specific tests 593 tests = append(tests, winbasetests...) 594 } 595 for _, test := range tests { 596 if s := filepath.Base(test.path); s != test.result { 597 t.Errorf("Base(%q) = %q, want %q", test.path, s, test.result) 598 } 599 } 600 } 601 602 var dirtests = []PathTest{ 603 {"", "."}, 604 {".", "."}, 605 {"/.", "/"}, 606 {"/", "/"}, 607 {"////", "/"}, 608 {"/foo", "/"}, 609 {"x/", "x"}, 610 {"abc", "."}, 611 {"abc/def", "abc"}, 612 {"a/b/.x", "a/b"}, 613 {"a/b/c.", "a/b"}, 614 {"a/b/c.x", "a/b"}, 615 } 616 617 var windirtests = []PathTest{ 618 {`c:\`, `c:\`}, 619 {`c:.`, `c:.`}, 620 {`c:\a\b`, `c:\a`}, 621 {`c:a\b`, `c:a`}, 622 {`c:a\b\c`, `c:a\b`}, 623 {`\\host\share\`, `\\host\share\`}, 624 {`\\host\share\a`, `\\host\share\`}, 625 {`\\host\share\a\b`, `\\host\share\a`}, 626 } 627 628 func TestDir(t *testing.T) { 629 tests := dirtests 630 if runtime.GOOS == "windows" { 631 // make unix tests work on windows 632 for i := range tests { 633 tests[i].result = filepath.Clean(tests[i].result) 634 } 635 // add windows specific tests 636 tests = append(tests, windirtests...) 637 } 638 for _, test := range tests { 639 if s := filepath.Dir(test.path); s != test.result { 640 t.Errorf("Dir(%q) = %q, want %q", test.path, s, test.result) 641 } 642 } 643 } 644 645 type IsAbsTest struct { 646 path string 647 isAbs bool 648 } 649 650 var isabstests = []IsAbsTest{ 651 {"", false}, 652 {"/", true}, 653 {"/usr/bin/gcc", true}, 654 {"..", false}, 655 {"/a/../bb", true}, 656 {".", false}, 657 {"./", false}, 658 {"lala", false}, 659 } 660 661 var winisabstests = []IsAbsTest{ 662 {`C:\`, true}, 663 {`c\`, false}, 664 {`c::`, false}, 665 {`c:`, false}, 666 {`/`, false}, 667 {`\`, false}, 668 {`\Windows`, false}, 669 {`c:a\b`, false}, 670 {`c:\a\b`, true}, 671 {`c:/a/b`, true}, 672 {`\\host\share\foo`, true}, 673 {`//host/share/foo/bar`, true}, 674 } 675 676 func TestIsAbs(t *testing.T) { 677 var tests []IsAbsTest 678 if runtime.GOOS == "windows" { 679 tests = append(tests, winisabstests...) 680 // All non-windows tests should fail, because they have no volume letter. 681 for _, test := range isabstests { 682 tests = append(tests, IsAbsTest{test.path, false}) 683 } 684 // All non-windows test should work as intended if prefixed with volume letter. 685 for _, test := range isabstests { 686 tests = append(tests, IsAbsTest{"c:" + test.path, test.isAbs}) 687 } 688 } else { 689 tests = isabstests 690 } 691 692 for _, test := range tests { 693 if r := filepath.IsAbs(test.path); r != test.isAbs { 694 t.Errorf("IsAbs(%q) = %v, want %v", test.path, r, test.isAbs) 695 } 696 } 697 } 698 699 type EvalSymlinksTest struct { 700 // If dest is empty, the path is created; otherwise the dest is symlinked to the path. 701 path, dest string 702 } 703 704 var EvalSymlinksTestDirs = []EvalSymlinksTest{ 705 {"test", ""}, 706 {"test/dir", ""}, 707 {"test/dir/link3", "../../"}, 708 {"test/link1", "../test"}, 709 {"test/link2", "dir"}, 710 {"test/linkabs", "/"}, 711 } 712 713 var EvalSymlinksTests = []EvalSymlinksTest{ 714 {"test", "test"}, 715 {"test/dir", "test/dir"}, 716 {"test/dir/../..", "."}, 717 {"test/link1", "test"}, 718 {"test/link2", "test/dir"}, 719 {"test/link1/dir", "test/dir"}, 720 {"test/link2/..", "test"}, 721 {"test/dir/link3", "."}, 722 {"test/link2/link3/test", "test"}, 723 {"test/linkabs", "/"}, 724 } 725 726 var EvalSymlinksAbsWindowsTests = []EvalSymlinksTest{ 727 {`c:\`, `c:\`}, 728 } 729 730 // simpleJoin builds a file name from the directory and path. 731 // It does not use Join because we don't want ".." to be evaluated. 732 func simpleJoin(dir, path string) string { 733 return dir + string(filepath.Separator) + path 734 } 735 736 func TestEvalSymlinks(t *testing.T) { 737 switch runtime.GOOS { 738 case "nacl", "plan9": 739 t.Skipf("skipping on %s", runtime.GOOS) 740 } 741 742 tmpDir, err := ioutil.TempDir("", "evalsymlink") 743 if err != nil { 744 t.Fatal("creating temp dir:", err) 745 } 746 defer os.RemoveAll(tmpDir) 747 748 // /tmp may itself be a symlink! Avoid the confusion, although 749 // it means trusting the thing we're testing. 750 tmpDir, err = filepath.EvalSymlinks(tmpDir) 751 if err != nil { 752 t.Fatal("eval symlink for tmp dir:", err) 753 } 754 755 // Create the symlink farm using relative paths. 756 for _, d := range EvalSymlinksTestDirs { 757 var err error 758 path := simpleJoin(tmpDir, d.path) 759 if d.dest == "" { 760 err = os.Mkdir(path, 0755) 761 } else { 762 if supportsSymlinks { 763 err = os.Symlink(d.dest, path) 764 } 765 } 766 if err != nil { 767 t.Fatal(err) 768 } 769 } 770 771 var tests []EvalSymlinksTest 772 if supportsSymlinks { 773 tests = EvalSymlinksTests 774 } else { 775 for _, d := range EvalSymlinksTests { 776 if d.path == d.dest { 777 // will test only real files and directories 778 tests = append(tests, d) 779 // test "canonical" names 780 d2 := EvalSymlinksTest{ 781 path: strings.ToUpper(d.path), 782 dest: d.dest, 783 } 784 tests = append(tests, d2) 785 } 786 } 787 } 788 789 // Evaluate the symlink farm. 790 for _, d := range tests { 791 path := simpleJoin(tmpDir, d.path) 792 dest := simpleJoin(tmpDir, d.dest) 793 if filepath.IsAbs(d.dest) || os.IsPathSeparator(d.dest[0]) { 794 dest = d.dest 795 } 796 if p, err := filepath.EvalSymlinks(path); err != nil { 797 t.Errorf("EvalSymlinks(%q) error: %v", d.path, err) 798 } else if filepath.Clean(p) != filepath.Clean(dest) { 799 t.Errorf("Clean(%q)=%q, want %q", path, p, dest) 800 } 801 } 802 } 803 804 // Test directories relative to temporary directory. 805 // The tests are run in absTestDirs[0]. 806 var absTestDirs = []string{ 807 "a", 808 "a/b", 809 "a/b/c", 810 } 811 812 // Test paths relative to temporary directory. $ expands to the directory. 813 // The tests are run in absTestDirs[0]. 814 // We create absTestDirs first. 815 var absTests = []string{ 816 ".", 817 "b", 818 "../a", 819 "../a/b", 820 "../a/b/./c/../../.././a", 821 "$", 822 "$/.", 823 "$/a/../a/b", 824 "$/a/b/c/../../.././a", 825 } 826 827 func TestAbs(t *testing.T) { 828 root, err := ioutil.TempDir("", "TestAbs") 829 if err != nil { 830 t.Fatal("TempDir failed: ", err) 831 } 832 defer os.RemoveAll(root) 833 834 wd, err := os.Getwd() 835 if err != nil { 836 t.Fatal("getwd failed: ", err) 837 } 838 err = os.Chdir(root) 839 if err != nil { 840 t.Fatal("chdir failed: ", err) 841 } 842 defer os.Chdir(wd) 843 844 for _, dir := range absTestDirs { 845 err = os.Mkdir(dir, 0777) 846 if err != nil { 847 t.Fatal("Mkdir failed: ", err) 848 } 849 } 850 851 if runtime.GOOS == "windows" { 852 vol := filepath.VolumeName(root) 853 var extra []string 854 for _, path := range absTests { 855 if strings.Index(path, "$") != -1 { 856 continue 857 } 858 path = vol + path 859 extra = append(extra, path) 860 } 861 absTests = append(absTests, extra...) 862 } 863 864 err = os.Chdir(absTestDirs[0]) 865 if err != nil { 866 t.Fatal("chdir failed: ", err) 867 } 868 869 for _, path := range absTests { 870 path = strings.Replace(path, "$", root, -1) 871 info, err := os.Stat(path) 872 if err != nil { 873 t.Errorf("%s: %s", path, err) 874 continue 875 } 876 877 abspath, err := filepath.Abs(path) 878 if err != nil { 879 t.Errorf("Abs(%q) error: %v", path, err) 880 continue 881 } 882 absinfo, err := os.Stat(abspath) 883 if err != nil || !os.SameFile(absinfo, info) { 884 t.Errorf("Abs(%q)=%q, not the same file", path, abspath) 885 } 886 if !filepath.IsAbs(abspath) { 887 t.Errorf("Abs(%q)=%q, not an absolute path", path, abspath) 888 } 889 if filepath.IsAbs(path) && abspath != filepath.Clean(path) { 890 t.Errorf("Abs(%q)=%q, isn't clean", path, abspath) 891 } 892 } 893 } 894 895 type RelTests struct { 896 root, path, want string 897 } 898 899 var reltests = []RelTests{ 900 {"a/b", "a/b", "."}, 901 {"a/b/.", "a/b", "."}, 902 {"a/b", "a/b/.", "."}, 903 {"./a/b", "a/b", "."}, 904 {"a/b", "./a/b", "."}, 905 {"ab/cd", "ab/cde", "../cde"}, 906 {"ab/cd", "ab/c", "../c"}, 907 {"a/b", "a/b/c/d", "c/d"}, 908 {"a/b", "a/b/../c", "../c"}, 909 {"a/b/../c", "a/b", "../b"}, 910 {"a/b/c", "a/c/d", "../../c/d"}, 911 {"a/b", "c/d", "../../c/d"}, 912 {"a/b/c/d", "a/b", "../.."}, 913 {"a/b/c/d", "a/b/", "../.."}, 914 {"a/b/c/d/", "a/b", "../.."}, 915 {"a/b/c/d/", "a/b/", "../.."}, 916 {"../../a/b", "../../a/b/c/d", "c/d"}, 917 {"/a/b", "/a/b", "."}, 918 {"/a/b/.", "/a/b", "."}, 919 {"/a/b", "/a/b/.", "."}, 920 {"/ab/cd", "/ab/cde", "../cde"}, 921 {"/ab/cd", "/ab/c", "../c"}, 922 {"/a/b", "/a/b/c/d", "c/d"}, 923 {"/a/b", "/a/b/../c", "../c"}, 924 {"/a/b/../c", "/a/b", "../b"}, 925 {"/a/b/c", "/a/c/d", "../../c/d"}, 926 {"/a/b", "/c/d", "../../c/d"}, 927 {"/a/b/c/d", "/a/b", "../.."}, 928 {"/a/b/c/d", "/a/b/", "../.."}, 929 {"/a/b/c/d/", "/a/b", "../.."}, 930 {"/a/b/c/d/", "/a/b/", "../.."}, 931 {"/../../a/b", "/../../a/b/c/d", "c/d"}, 932 {".", "a/b", "a/b"}, 933 {".", "..", ".."}, 934 935 // can't do purely lexically 936 {"..", ".", "err"}, 937 {"..", "a", "err"}, 938 {"../..", "..", "err"}, 939 {"a", "/a", "err"}, 940 {"/a", "a", "err"}, 941 } 942 943 var winreltests = []RelTests{ 944 {`C:a\b\c`, `C:a/b/d`, `..\d`}, 945 {`C:\`, `D:\`, `err`}, 946 {`C:`, `D:`, `err`}, 947 } 948 949 func TestRel(t *testing.T) { 950 tests := append([]RelTests{}, reltests...) 951 if runtime.GOOS == "windows" { 952 for i := range tests { 953 tests[i].want = filepath.FromSlash(tests[i].want) 954 } 955 tests = append(tests, winreltests...) 956 } 957 for _, test := range tests { 958 got, err := filepath.Rel(test.root, test.path) 959 if test.want == "err" { 960 if err == nil { 961 t.Errorf("Rel(%q, %q)=%q, want error", test.root, test.path, got) 962 } 963 continue 964 } 965 if err != nil { 966 t.Errorf("Rel(%q, %q): want %q, got error: %s", test.root, test.path, test.want, err) 967 } 968 if got != test.want { 969 t.Errorf("Rel(%q, %q)=%q, want %q", test.root, test.path, got, test.want) 970 } 971 } 972 } 973 974 type VolumeNameTest struct { 975 path string 976 vol string 977 } 978 979 var volumenametests = []VolumeNameTest{ 980 {`c:/foo/bar`, `c:`}, 981 {`c:`, `c:`}, 982 {`2:`, ``}, 983 {``, ``}, 984 {`\\\host`, ``}, 985 {`\\\host\`, ``}, 986 {`\\\host\share`, ``}, 987 {`\\\host\\share`, ``}, 988 {`\\host`, ``}, 989 {`//host`, ``}, 990 {`\\host\`, ``}, 991 {`//host/`, ``}, 992 {`\\host\share`, `\\host\share`}, 993 {`//host/share`, `//host/share`}, 994 {`\\host\share\`, `\\host\share`}, 995 {`//host/share/`, `//host/share`}, 996 {`\\host\share\foo`, `\\host\share`}, 997 {`//host/share/foo`, `//host/share`}, 998 {`\\host\share\\foo\\\bar\\\\baz`, `\\host\share`}, 999 {`//host/share//foo///bar////baz`, `//host/share`}, 1000 {`\\host\share\foo\..\bar`, `\\host\share`}, 1001 {`//host/share/foo/../bar`, `//host/share`}, 1002 } 1003 1004 func TestVolumeName(t *testing.T) { 1005 if runtime.GOOS != "windows" { 1006 return 1007 } 1008 for _, v := range volumenametests { 1009 if vol := filepath.VolumeName(v.path); vol != v.vol { 1010 t.Errorf("VolumeName(%q)=%q, want %q", v.path, vol, v.vol) 1011 } 1012 } 1013 } 1014 1015 func TestDriveLetterInEvalSymlinks(t *testing.T) { 1016 if runtime.GOOS != "windows" { 1017 return 1018 } 1019 wd, _ := os.Getwd() 1020 if len(wd) < 3 { 1021 t.Errorf("Current directory path %q is too short", wd) 1022 } 1023 lp := strings.ToLower(wd) 1024 up := strings.ToUpper(wd) 1025 flp, err := filepath.EvalSymlinks(lp) 1026 if err != nil { 1027 t.Fatalf("EvalSymlinks(%q) failed: %q", lp, err) 1028 } 1029 fup, err := filepath.EvalSymlinks(up) 1030 if err != nil { 1031 t.Fatalf("EvalSymlinks(%q) failed: %q", up, err) 1032 } 1033 if flp != fup { 1034 t.Errorf("Results of EvalSymlinks do not match: %q and %q", flp, fup) 1035 } 1036 } 1037 1038 func TestBug3486(t *testing.T) { // http://golang.org/issue/3486 1039 if runtime.GOOS == "darwin" { 1040 switch runtime.GOARCH { 1041 case "arm", "arm64": 1042 t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH) 1043 } 1044 } 1045 root, err := filepath.EvalSymlinks(runtime.GOROOT() + "/test") 1046 if err != nil { 1047 t.Fatal(err) 1048 } 1049 bugs := filepath.Join(root, "bugs") 1050 ken := filepath.Join(root, "ken") 1051 seenBugs := false 1052 seenKen := false 1053 filepath.Walk(root, func(pth string, info os.FileInfo, err error) error { 1054 if err != nil { 1055 t.Fatal(err) 1056 } 1057 1058 switch pth { 1059 case bugs: 1060 seenBugs = true 1061 return filepath.SkipDir 1062 case ken: 1063 if !seenBugs { 1064 t.Fatal("filepath.Walk out of order - ken before bugs") 1065 } 1066 seenKen = true 1067 } 1068 return nil 1069 }) 1070 if !seenKen { 1071 t.Fatalf("%q not seen", ken) 1072 } 1073 }