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