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