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