github.com/golang/dep@v0.5.4/internal/fs/fs_test.go (about) 1 // Copyright 2016 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 fs 6 7 import ( 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "reflect" 12 "runtime" 13 "strings" 14 "testing" 15 16 "github.com/golang/dep/internal/test" 17 "github.com/pkg/errors" 18 ) 19 20 // This function tests HadFilepathPrefix. It should test it on both case 21 // sensitive and insensitive situations. However, the only reliable way to test 22 // case-insensitive behaviour is if using case-insensitive filesystem. This 23 // cannot be guaranteed in an automated test. Therefore, the behaviour of the 24 // tests is not to test case sensitivity on *nix and to assume that Windows is 25 // case-insensitive. Please see link below for some background. 26 // 27 // https://superuser.com/questions/266110/how-do-you-make-windows-7-fully-case-sensitive-with-respect-to-the-filesystem 28 // 29 // NOTE: NTFS can be made case-sensitive. However many Windows programs, 30 // including Windows Explorer do not handle gracefully multiple files that 31 // differ only in capitalization. It is possible that this can cause these tests 32 // to fail on some setups. 33 func TestHasFilepathPrefix(t *testing.T) { 34 dir, err := ioutil.TempDir("", "dep") 35 if err != nil { 36 t.Fatal(err) 37 } 38 defer os.RemoveAll(dir) 39 40 // dir2 is the same as dir but with different capitalization on Windows to 41 // test case insensitivity 42 var dir2 string 43 if runtime.GOOS == "windows" { 44 dir = strings.ToLower(dir) 45 dir2 = strings.ToUpper(dir) 46 } else { 47 dir2 = dir 48 } 49 50 // For testing trailing and repeated separators 51 sep := string(os.PathSeparator) 52 53 cases := []struct { 54 path string 55 prefix string 56 want bool 57 }{ 58 {filepath.Join(dir, "a", "b"), filepath.Join(dir2), true}, 59 {filepath.Join(dir, "a", "b"), dir2 + sep + sep + "a", true}, 60 {filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a") + sep, true}, 61 {filepath.Join(dir, "a", "b") + sep, filepath.Join(dir2), true}, 62 {dir + sep + sep + filepath.Join("a", "b"), filepath.Join(dir2, "a"), true}, 63 {filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a"), true}, 64 {filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a", "b"), true}, 65 {filepath.Join(dir, "a", "b"), filepath.Join(dir2, "c"), false}, 66 {filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a", "d", "b"), false}, 67 {filepath.Join(dir, "a", "b"), filepath.Join(dir2, "a", "b2"), false}, 68 {filepath.Join(dir), filepath.Join(dir2, "a", "b"), false}, 69 {filepath.Join(dir, "ab"), filepath.Join(dir2, "a", "b"), false}, 70 {filepath.Join(dir, "ab"), filepath.Join(dir2, "a"), false}, 71 {filepath.Join(dir, "123"), filepath.Join(dir2, "123"), true}, 72 {filepath.Join(dir, "123"), filepath.Join(dir2, "1"), false}, 73 {filepath.Join(dir, "⌘"), filepath.Join(dir2, "⌘"), true}, 74 {filepath.Join(dir, "a"), filepath.Join(dir2, "⌘"), false}, 75 {filepath.Join(dir, "⌘"), filepath.Join(dir2, "a"), false}, 76 } 77 78 for _, c := range cases { 79 if err := os.MkdirAll(c.path, 0755); err != nil { 80 t.Fatal(err) 81 } 82 83 if err = os.MkdirAll(c.prefix, 0755); err != nil { 84 t.Fatal(err) 85 } 86 87 got, err := HasFilepathPrefix(c.path, c.prefix) 88 if err != nil { 89 t.Fatalf("unexpected error: %s", err) 90 } 91 if c.want != got { 92 t.Fatalf("dir: %q, prefix: %q, expected: %v, got: %v", c.path, c.prefix, c.want, got) 93 } 94 } 95 } 96 97 // This function tests HadFilepathPrefix. It should test it on both case 98 // sensitive and insensitive situations. However, the only reliable way to test 99 // case-insensitive behaviour is if using case-insensitive filesystem. This 100 // cannot be guaranteed in an automated test. Therefore, the behaviour of the 101 // tests is not to test case sensitivity on *nix and to assume that Windows is 102 // case-insensitive. Please see link below for some background. 103 // 104 // https://superuser.com/questions/266110/how-do-you-make-windows-7-fully-case-sensitive-with-respect-to-the-filesystem 105 // 106 // NOTE: NTFS can be made case-sensitive. However many Windows programs, 107 // including Windows Explorer do not handle gracefully multiple files that 108 // differ only in capitalization. It is possible that this can cause these tests 109 // to fail on some setups. 110 func TestHasFilepathPrefix_Files(t *testing.T) { 111 dir, err := ioutil.TempDir("", "dep") 112 if err != nil { 113 t.Fatal(err) 114 } 115 defer os.RemoveAll(dir) 116 117 // dir2 is the same as dir but with different capitalization on Windows to 118 // test case insensitivity 119 var dir2 string 120 if runtime.GOOS == "windows" { 121 dir = strings.ToLower(dir) 122 dir2 = strings.ToUpper(dir) 123 } else { 124 dir2 = dir 125 } 126 127 existingFile := filepath.Join(dir, "exists") 128 if err = os.MkdirAll(existingFile, 0755); err != nil { 129 t.Fatal(err) 130 } 131 132 nonExistingFile := filepath.Join(dir, "does_not_exists") 133 134 cases := []struct { 135 path string 136 prefix string 137 want bool 138 err bool 139 }{ 140 {existingFile, filepath.Join(dir2), true, false}, 141 {nonExistingFile, filepath.Join(dir2), false, true}, 142 } 143 144 for _, c := range cases { 145 got, err := HasFilepathPrefix(c.path, c.prefix) 146 if err != nil && !c.err { 147 t.Fatalf("unexpected error: %s", err) 148 } 149 if c.want != got { 150 t.Fatalf("dir: %q, prefix: %q, expected: %v, got: %v", c.path, c.prefix, c.want, got) 151 } 152 } 153 } 154 155 func TestEquivalentPaths(t *testing.T) { 156 h := test.NewHelper(t) 157 h.TempDir("dir") 158 h.TempDir("dir2") 159 160 h.TempFile("file", "") 161 h.TempFile("file2", "") 162 163 h.TempDir("DIR") 164 h.TempFile("FILE", "") 165 166 testcases := []struct { 167 p1, p2 string 168 caseSensitiveEquivalent bool 169 caseInensitiveEquivalent bool 170 err bool 171 }{ 172 {h.Path("dir"), h.Path("dir"), true, true, false}, 173 {h.Path("file"), h.Path("file"), true, true, false}, 174 {h.Path("dir"), h.Path("dir2"), false, false, false}, 175 {h.Path("file"), h.Path("file2"), false, false, false}, 176 {h.Path("dir"), h.Path("file"), false, false, false}, 177 {h.Path("dir"), h.Path("DIR"), false, true, false}, 178 {strings.ToLower(h.Path("dir")), strings.ToUpper(h.Path("dir")), false, true, true}, 179 } 180 181 caseSensitive, err := IsCaseSensitiveFilesystem(h.Path("dir")) 182 if err != nil { 183 t.Fatal("unexpcted error:", err) 184 } 185 186 for _, tc := range testcases { 187 got, err := EquivalentPaths(tc.p1, tc.p2) 188 if err != nil && !tc.err { 189 t.Error("unexpected error:", err) 190 } 191 if caseSensitive { 192 if tc.caseSensitiveEquivalent != got { 193 t.Errorf("expected EquivalentPaths(%q, %q) to be %t on case-sensitive filesystem, got %t", tc.p1, tc.p2, tc.caseSensitiveEquivalent, got) 194 } 195 } else { 196 if tc.caseInensitiveEquivalent != got { 197 t.Errorf("expected EquivalentPaths(%q, %q) to be %t on case-insensitive filesystem, got %t", tc.p1, tc.p2, tc.caseInensitiveEquivalent, got) 198 } 199 } 200 } 201 } 202 203 func TestRenameWithFallback(t *testing.T) { 204 dir, err := ioutil.TempDir("", "dep") 205 if err != nil { 206 t.Fatal(err) 207 } 208 defer os.RemoveAll(dir) 209 210 if err = RenameWithFallback(filepath.Join(dir, "does_not_exists"), filepath.Join(dir, "dst")); err == nil { 211 t.Fatal("expected an error for non existing file, but got nil") 212 } 213 214 srcpath := filepath.Join(dir, "src") 215 216 if srcf, err := os.Create(srcpath); err != nil { 217 t.Fatal(err) 218 } else { 219 srcf.Close() 220 } 221 222 if err = RenameWithFallback(srcpath, filepath.Join(dir, "dst")); err != nil { 223 t.Fatal(err) 224 } 225 226 srcpath = filepath.Join(dir, "a") 227 if err = os.MkdirAll(srcpath, 0777); err != nil { 228 t.Fatal(err) 229 } 230 231 dstpath := filepath.Join(dir, "b") 232 if err = os.MkdirAll(dstpath, 0777); err != nil { 233 t.Fatal(err) 234 } 235 236 if err = RenameWithFallback(srcpath, dstpath); err == nil { 237 t.Fatal("expected an error if dst is an existing directory, but got nil") 238 } 239 } 240 241 func TestIsCaseSensitiveFilesystem(t *testing.T) { 242 isLinux := runtime.GOOS == "linux" 243 isWindows := runtime.GOOS == "windows" 244 isMacOS := runtime.GOOS == "darwin" 245 246 if !isLinux && !isWindows && !isMacOS { 247 t.Skip("Run this test on Windows, Linux and macOS only") 248 } 249 250 dir, err := ioutil.TempDir("", "TestCaseSensitivity") 251 if err != nil { 252 t.Fatal(err) 253 } 254 defer os.RemoveAll(dir) 255 256 var want bool 257 if isLinux { 258 want = true 259 } else { 260 want = false 261 } 262 263 got, err := IsCaseSensitiveFilesystem(dir) 264 265 if err != nil { 266 t.Fatalf("unexpected error message: \n\t(GOT) %+v", err) 267 } 268 269 if want != got { 270 t.Fatalf("unexpected value returned: \n\t(GOT) %t\n\t(WNT) %t", got, want) 271 } 272 } 273 274 func TestReadActualFilenames(t *testing.T) { 275 // We are trying to skip this test on file systems which are case-sensiive. We could 276 // have used `fs.IsCaseSensitiveFilesystem` for this check. However, the code we are 277 // testing also relies on `fs.IsCaseSensitiveFilesystem`. So a bug in 278 // `fs.IsCaseSensitiveFilesystem` could prevent this test from being run. This is the 279 // only scenario where we prefer the OS heuristic over doing the actual work of 280 // validating filesystem case sensitivity via `fs.IsCaseSensitiveFilesystem`. 281 if runtime.GOOS != "windows" && runtime.GOOS != "darwin" { 282 t.Skip("skip this test on non-Windows, non-macOS") 283 } 284 285 h := test.NewHelper(t) 286 defer h.Cleanup() 287 288 h.TempDir("") 289 tmpPath := h.Path(".") 290 291 // First, check the scenarios for which we expect an error. 292 _, err := ReadActualFilenames(filepath.Join(tmpPath, "does_not_exists"), []string{""}) 293 switch { 294 case err == nil: 295 t.Fatal("expected err for non-existing folder") 296 // use `errors.Cause` because the error is wrapped and returned 297 case !os.IsNotExist(errors.Cause(err)): 298 t.Fatalf("unexpected error: %+v", err) 299 } 300 h.TempFile("tmpFile", "") 301 _, err = ReadActualFilenames(h.Path("tmpFile"), []string{""}) 302 switch { 303 case err == nil: 304 t.Fatal("expected err for passing file instead of directory") 305 case err != errPathNotDir: 306 t.Fatalf("unexpected error: %+v", err) 307 } 308 309 cases := []struct { 310 createFiles []string 311 names []string 312 want map[string]string 313 }{ 314 // If we supply no filenames to the function, it should return an empty map. 315 {nil, nil, map[string]string{}}, 316 // If the directory contains the given file with different case, it should return 317 // a map which has the given filename as the key and actual filename as the value. 318 { 319 []string{"test1.txt"}, 320 []string{"Test1.txt"}, 321 map[string]string{"Test1.txt": "test1.txt"}, 322 }, 323 // 1. If the given filename is same as the actual filename, map should have the 324 // same key and value for the file. 325 // 2. If the given filename is present with different case for file extension, 326 // it should return a map which has the given filename as the key and actual 327 // filename as the value. 328 // 3. If the given filename is not present even with a different case, the map 329 // returned should not have an entry for that filename. 330 { 331 []string{"test2.txt", "test3.TXT"}, 332 []string{"test2.txt", "Test3.txt", "Test4.txt"}, 333 map[string]string{ 334 "test2.txt": "test2.txt", 335 "Test3.txt": "test3.TXT", 336 }, 337 }, 338 } 339 for _, c := range cases { 340 for _, file := range c.createFiles { 341 h.TempFile(file, "") 342 } 343 got, err := ReadActualFilenames(tmpPath, c.names) 344 if err != nil { 345 t.Fatalf("unexpected error: %+v", err) 346 } 347 if !reflect.DeepEqual(c.want, got) { 348 t.Fatalf("returned value does not match expected: \n\t(GOT) %v\n\t(WNT) %v", 349 got, c.want) 350 } 351 } 352 } 353 354 func TestGenTestFilename(t *testing.T) { 355 cases := []struct { 356 str string 357 want string 358 }{ 359 {"abc", "Abc"}, 360 {"ABC", "aBC"}, 361 {"AbC", "abC"}, 362 {"αβγ", "Αβγ"}, 363 {"123", "123"}, 364 {"1a2", "1A2"}, 365 {"12a", "12A"}, 366 {"⌘", "⌘"}, 367 } 368 369 for _, c := range cases { 370 got := genTestFilename(c.str) 371 if c.want != got { 372 t.Fatalf("str: %q, expected: %q, got: %q", c.str, c.want, got) 373 } 374 } 375 } 376 377 func BenchmarkGenTestFilename(b *testing.B) { 378 cases := []string{ 379 strings.Repeat("a", 128), 380 strings.Repeat("A", 128), 381 strings.Repeat("α", 128), 382 strings.Repeat("1", 128), 383 strings.Repeat("⌘", 128), 384 } 385 386 for i := 0; i < b.N; i++ { 387 for _, str := range cases { 388 genTestFilename(str) 389 } 390 } 391 } 392 393 func TestCopyDir(t *testing.T) { 394 dir, err := ioutil.TempDir("", "dep") 395 if err != nil { 396 t.Fatal(err) 397 } 398 defer os.RemoveAll(dir) 399 400 srcdir := filepath.Join(dir, "src") 401 if err := os.MkdirAll(srcdir, 0755); err != nil { 402 t.Fatal(err) 403 } 404 405 files := []struct { 406 path string 407 contents string 408 fi os.FileInfo 409 }{ 410 {path: "myfile", contents: "hello world"}, 411 {path: filepath.Join("subdir", "file"), contents: "subdir file"}, 412 } 413 414 // Create structure indicated in 'files' 415 for i, file := range files { 416 fn := filepath.Join(srcdir, file.path) 417 dn := filepath.Dir(fn) 418 if err = os.MkdirAll(dn, 0755); err != nil { 419 t.Fatal(err) 420 } 421 422 fh, err := os.Create(fn) 423 if err != nil { 424 t.Fatal(err) 425 } 426 427 if _, err = fh.Write([]byte(file.contents)); err != nil { 428 t.Fatal(err) 429 } 430 fh.Close() 431 432 files[i].fi, err = os.Stat(fn) 433 if err != nil { 434 t.Fatal(err) 435 } 436 } 437 438 destdir := filepath.Join(dir, "dest") 439 if err := CopyDir(srcdir, destdir); err != nil { 440 t.Fatal(err) 441 } 442 443 // Compare copy against structure indicated in 'files' 444 for _, file := range files { 445 fn := filepath.Join(srcdir, file.path) 446 dn := filepath.Dir(fn) 447 dirOK, err := IsDir(dn) 448 if err != nil { 449 t.Fatal(err) 450 } 451 if !dirOK { 452 t.Fatalf("expected %s to be a directory", dn) 453 } 454 455 got, err := ioutil.ReadFile(fn) 456 if err != nil { 457 t.Fatal(err) 458 } 459 460 if file.contents != string(got) { 461 t.Fatalf("expected: %s, got: %s", file.contents, string(got)) 462 } 463 464 gotinfo, err := os.Stat(fn) 465 if err != nil { 466 t.Fatal(err) 467 } 468 469 if file.fi.Mode() != gotinfo.Mode() { 470 t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v", 471 file.path, file.fi.Mode(), fn, gotinfo.Mode()) 472 } 473 } 474 } 475 476 func TestCopyDirFail_SrcInaccessible(t *testing.T) { 477 if runtime.GOOS == "windows" { 478 // XXX: setting permissions works differently in 479 // Microsoft Windows. Skipping this this until a 480 // compatible implementation is provided. 481 t.Skip("skipping on windows") 482 } 483 484 var srcdir, dstdir string 485 486 cleanup := setupInaccessibleDir(t, func(dir string) error { 487 srcdir = filepath.Join(dir, "src") 488 return os.MkdirAll(srcdir, 0755) 489 }) 490 defer cleanup() 491 492 dir, err := ioutil.TempDir("", "dep") 493 if err != nil { 494 t.Fatal(err) 495 } 496 defer os.RemoveAll(dir) 497 498 dstdir = filepath.Join(dir, "dst") 499 if err = CopyDir(srcdir, dstdir); err == nil { 500 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 501 } 502 } 503 504 func TestCopyDirFail_DstInaccessible(t *testing.T) { 505 if runtime.GOOS == "windows" { 506 // XXX: setting permissions works differently in 507 // Microsoft Windows. Skipping this this until a 508 // compatible implementation is provided. 509 t.Skip("skipping on windows") 510 } 511 512 var srcdir, dstdir string 513 514 dir, err := ioutil.TempDir("", "dep") 515 if err != nil { 516 t.Fatal(err) 517 } 518 defer os.RemoveAll(dir) 519 520 srcdir = filepath.Join(dir, "src") 521 if err = os.MkdirAll(srcdir, 0755); err != nil { 522 t.Fatal(err) 523 } 524 525 cleanup := setupInaccessibleDir(t, func(dir string) error { 526 dstdir = filepath.Join(dir, "dst") 527 return nil 528 }) 529 defer cleanup() 530 531 if err := CopyDir(srcdir, dstdir); err == nil { 532 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 533 } 534 } 535 536 func TestCopyDirFail_SrcIsNotDir(t *testing.T) { 537 var srcdir, dstdir string 538 539 dir, err := ioutil.TempDir("", "dep") 540 if err != nil { 541 t.Fatal(err) 542 } 543 defer os.RemoveAll(dir) 544 545 srcdir = filepath.Join(dir, "src") 546 if _, err = os.Create(srcdir); err != nil { 547 t.Fatal(err) 548 } 549 550 dstdir = filepath.Join(dir, "dst") 551 552 if err = CopyDir(srcdir, dstdir); err == nil { 553 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 554 } 555 556 if err != errSrcNotDir { 557 t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errSrcNotDir, srcdir, dstdir, err) 558 } 559 560 } 561 562 func TestCopyDirFail_DstExists(t *testing.T) { 563 var srcdir, dstdir string 564 565 dir, err := ioutil.TempDir("", "dep") 566 if err != nil { 567 t.Fatal(err) 568 } 569 defer os.RemoveAll(dir) 570 571 srcdir = filepath.Join(dir, "src") 572 if err = os.MkdirAll(srcdir, 0755); err != nil { 573 t.Fatal(err) 574 } 575 576 dstdir = filepath.Join(dir, "dst") 577 if err = os.MkdirAll(dstdir, 0755); err != nil { 578 t.Fatal(err) 579 } 580 581 if err = CopyDir(srcdir, dstdir); err == nil { 582 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 583 } 584 585 if err != errDstExist { 586 t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errDstExist, srcdir, dstdir, err) 587 } 588 } 589 590 func TestCopyDirFailOpen(t *testing.T) { 591 if runtime.GOOS == "windows" { 592 // XXX: setting permissions works differently in 593 // Microsoft Windows. os.Chmod(..., 0222) below is not 594 // enough for the file to be readonly, and os.Chmod(..., 595 // 0000) returns an invalid argument error. Skipping 596 // this this until a compatible implementation is 597 // provided. 598 t.Skip("skipping on windows") 599 } 600 601 var srcdir, dstdir string 602 603 dir, err := ioutil.TempDir("", "dep") 604 if err != nil { 605 t.Fatal(err) 606 } 607 defer os.RemoveAll(dir) 608 609 srcdir = filepath.Join(dir, "src") 610 if err = os.MkdirAll(srcdir, 0755); err != nil { 611 t.Fatal(err) 612 } 613 614 srcfn := filepath.Join(srcdir, "file") 615 srcf, err := os.Create(srcfn) 616 if err != nil { 617 t.Fatal(err) 618 } 619 srcf.Close() 620 621 // setup source file so that it cannot be read 622 if err = os.Chmod(srcfn, 0222); err != nil { 623 t.Fatal(err) 624 } 625 626 dstdir = filepath.Join(dir, "dst") 627 628 if err = CopyDir(srcdir, dstdir); err == nil { 629 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 630 } 631 } 632 633 func TestCopyFile(t *testing.T) { 634 dir, err := ioutil.TempDir("", "dep") 635 if err != nil { 636 t.Fatal(err) 637 } 638 defer os.RemoveAll(dir) 639 640 srcf, err := os.Create(filepath.Join(dir, "srcfile")) 641 if err != nil { 642 t.Fatal(err) 643 } 644 645 want := "hello world" 646 if _, err := srcf.Write([]byte(want)); err != nil { 647 t.Fatal(err) 648 } 649 srcf.Close() 650 651 destf := filepath.Join(dir, "destf") 652 if err := copyFile(srcf.Name(), destf); err != nil { 653 t.Fatal(err) 654 } 655 656 got, err := ioutil.ReadFile(destf) 657 if err != nil { 658 t.Fatal(err) 659 } 660 661 if want != string(got) { 662 t.Fatalf("expected: %s, got: %s", want, string(got)) 663 } 664 665 wantinfo, err := os.Stat(srcf.Name()) 666 if err != nil { 667 t.Fatal(err) 668 } 669 670 gotinfo, err := os.Stat(destf) 671 if err != nil { 672 t.Fatal(err) 673 } 674 675 if wantinfo.Mode() != gotinfo.Mode() { 676 t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v", srcf.Name(), wantinfo.Mode(), destf, gotinfo.Mode()) 677 } 678 } 679 680 func TestCopyFileSymlink(t *testing.T) { 681 h := test.NewHelper(t) 682 defer h.Cleanup() 683 h.TempDir(".") 684 685 testcases := map[string]string{ 686 filepath.Join("./testdata/symlinks/file-symlink"): filepath.Join(h.Path("."), "dst-file"), 687 filepath.Join("./testdata/symlinks/windows-file-symlink"): filepath.Join(h.Path("."), "windows-dst-file"), 688 filepath.Join("./testdata/symlinks/invalid-symlink"): filepath.Join(h.Path("."), "invalid-symlink"), 689 } 690 691 for symlink, dst := range testcases { 692 t.Run(symlink, func(t *testing.T) { 693 var err error 694 if err = copyFile(symlink, dst); err != nil { 695 t.Fatalf("failed to copy symlink: %s", err) 696 } 697 698 var want, got string 699 700 if runtime.GOOS == "windows" { 701 // Creating symlinks on Windows require an additional permission 702 // regular users aren't granted usually. So we copy the file 703 // content as a fall back instead of creating a real symlink. 704 srcb, err := ioutil.ReadFile(symlink) 705 h.Must(err) 706 dstb, err := ioutil.ReadFile(dst) 707 h.Must(err) 708 709 want = string(srcb) 710 got = string(dstb) 711 } else { 712 want, err = os.Readlink(symlink) 713 h.Must(err) 714 715 got, err = os.Readlink(dst) 716 if err != nil { 717 t.Fatalf("could not resolve symlink: %s", err) 718 } 719 } 720 721 if want != got { 722 t.Fatalf("resolved path is incorrect. expected %s, got %s", want, got) 723 } 724 }) 725 } 726 } 727 728 func TestCopyFileLongFilePath(t *testing.T) { 729 if runtime.GOOS != "windows" { 730 // We want to ensure the temporary fix actually fixes the issue with 731 // os.Chmod and long file paths. This is only applicable on Windows. 732 t.Skip("skipping on non-windows") 733 } 734 735 h := test.NewHelper(t) 736 h.TempDir(".") 737 defer h.Cleanup() 738 739 tmpPath := h.Path(".") 740 741 // Create a directory with a long-enough path name to cause the bug in #774. 742 dirName := "" 743 for len(tmpPath+string(os.PathSeparator)+dirName) <= 300 { 744 dirName += "directory" 745 } 746 747 h.TempDir(dirName) 748 h.TempFile(dirName+string(os.PathSeparator)+"src", "") 749 750 tmpDirPath := tmpPath + string(os.PathSeparator) + dirName + string(os.PathSeparator) 751 752 err := copyFile(tmpDirPath+"src", tmpDirPath+"dst") 753 if err != nil { 754 t.Fatalf("unexpected error while copying file: %v", err) 755 } 756 } 757 758 // C:\Users\appveyor\AppData\Local\Temp\1\gotest639065787\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890\dir4567890 759 760 func TestCopyFileFail(t *testing.T) { 761 if runtime.GOOS == "windows" { 762 // XXX: setting permissions works differently in 763 // Microsoft Windows. Skipping this this until a 764 // compatible implementation is provided. 765 t.Skip("skipping on windows") 766 } 767 768 dir, err := ioutil.TempDir("", "dep") 769 if err != nil { 770 t.Fatal(err) 771 } 772 defer os.RemoveAll(dir) 773 774 srcf, err := os.Create(filepath.Join(dir, "srcfile")) 775 if err != nil { 776 t.Fatal(err) 777 } 778 srcf.Close() 779 780 var dstdir string 781 782 cleanup := setupInaccessibleDir(t, func(dir string) error { 783 dstdir = filepath.Join(dir, "dir") 784 return os.Mkdir(dstdir, 0777) 785 }) 786 defer cleanup() 787 788 fn := filepath.Join(dstdir, "file") 789 if err := copyFile(srcf.Name(), fn); err == nil { 790 t.Fatalf("expected error for %s, got none", fn) 791 } 792 } 793 794 // setupInaccessibleDir creates a temporary location with a single 795 // directory in it, in such a way that that directory is not accessible 796 // after this function returns. 797 // 798 // op is called with the directory as argument, so that it can create 799 // files or other test artifacts. 800 // 801 // If setupInaccessibleDir fails in its preparation, or op fails, t.Fatal 802 // will be invoked. 803 // 804 // This function returns a cleanup function that removes all the temporary 805 // files this function creates. It is the caller's responsibility to call 806 // this function before the test is done running, whether there's an error or not. 807 func setupInaccessibleDir(t *testing.T, op func(dir string) error) func() { 808 dir, err := ioutil.TempDir("", "dep") 809 if err != nil { 810 t.Fatal(err) 811 return nil // keep compiler happy 812 } 813 814 subdir := filepath.Join(dir, "dir") 815 816 cleanup := func() { 817 if err := os.Chmod(subdir, 0777); err != nil { 818 t.Error(err) 819 } 820 if err := os.RemoveAll(dir); err != nil { 821 t.Error(err) 822 } 823 } 824 825 if err := os.Mkdir(subdir, 0777); err != nil { 826 cleanup() 827 t.Fatal(err) 828 return nil 829 } 830 831 if err := op(subdir); err != nil { 832 cleanup() 833 t.Fatal(err) 834 return nil 835 } 836 837 if err := os.Chmod(subdir, 0666); err != nil { 838 cleanup() 839 t.Fatal(err) 840 return nil 841 } 842 843 return cleanup 844 } 845 846 func TestEnsureDir(t *testing.T) { 847 h := test.NewHelper(t) 848 defer h.Cleanup() 849 h.TempDir(".") 850 h.TempFile("file", "") 851 852 tmpPath := h.Path(".") 853 854 var dn string 855 cleanup := setupInaccessibleDir(t, func(dir string) error { 856 dn = filepath.Join(dir, "dir") 857 return os.Mkdir(dn, 0777) 858 }) 859 defer cleanup() 860 861 tests := map[string]bool{ 862 // [success] A dir already exists for the given path. 863 tmpPath: true, 864 // [success] Dir does not exist but parent dir exists, so should get created. 865 filepath.Join(tmpPath, "testdir"): true, 866 // [failure] Dir and parent dir do not exist, should return an error. 867 filepath.Join(tmpPath, "notexist", "testdir"): false, 868 // [failure] Regular file present at given path. 869 h.Path("file"): false, 870 // [failure] Path inaccessible. 871 dn: false, 872 } 873 874 if runtime.GOOS == "windows" { 875 // This test doesn't work on Microsoft Windows because 876 // of the differences in how file permissions are 877 // implemented. For this to work, the directory where 878 // the directory exists should be inaccessible. 879 delete(tests, dn) 880 } 881 882 for path, shouldEnsure := range tests { 883 err := EnsureDir(path, 0777) 884 if shouldEnsure { 885 if err != nil { 886 t.Fatalf("unexpected error %q for %q", err, path) 887 } else if ok, err := IsDir(path); !ok { 888 t.Fatalf("expected directory to be preset at %q", path) 889 t.Fatal(err) 890 } 891 } else if err == nil { 892 t.Fatalf("expected error for path %q, got none", path) 893 } 894 } 895 } 896 897 func TestIsRegular(t *testing.T) { 898 wd, err := os.Getwd() 899 if err != nil { 900 t.Fatal(err) 901 } 902 903 var fn string 904 905 cleanup := setupInaccessibleDir(t, func(dir string) error { 906 fn = filepath.Join(dir, "file") 907 fh, err := os.Create(fn) 908 if err != nil { 909 return err 910 } 911 912 return fh.Close() 913 }) 914 defer cleanup() 915 916 tests := map[string]struct { 917 exists bool 918 err bool 919 }{ 920 wd: {false, true}, 921 filepath.Join(wd, "testdata"): {false, true}, 922 filepath.Join(wd, "testdata", "test.file"): {true, false}, 923 filepath.Join(wd, "this_file_does_not_exist.thing"): {false, false}, 924 fn: {false, true}, 925 } 926 927 if runtime.GOOS == "windows" { 928 // This test doesn't work on Microsoft Windows because 929 // of the differences in how file permissions are 930 // implemented. For this to work, the directory where 931 // the file exists should be inaccessible. 932 delete(tests, fn) 933 } 934 935 for f, want := range tests { 936 got, err := IsRegular(f) 937 if err != nil { 938 if want.exists != got { 939 t.Fatalf("expected %t for %s, got %t", want.exists, f, got) 940 } 941 if !want.err { 942 t.Fatalf("expected no error, got %v", err) 943 } 944 } else { 945 if want.err { 946 t.Fatalf("expected error for %s, got none", f) 947 } 948 } 949 950 if got != want.exists { 951 t.Fatalf("expected %t for %s, got %t", want, f, got) 952 } 953 } 954 955 } 956 957 func TestIsDir(t *testing.T) { 958 wd, err := os.Getwd() 959 if err != nil { 960 t.Fatal(err) 961 } 962 963 var dn string 964 965 cleanup := setupInaccessibleDir(t, func(dir string) error { 966 dn = filepath.Join(dir, "dir") 967 return os.Mkdir(dn, 0777) 968 }) 969 defer cleanup() 970 971 tests := map[string]struct { 972 exists bool 973 err bool 974 }{ 975 wd: {true, false}, 976 filepath.Join(wd, "testdata"): {true, false}, 977 filepath.Join(wd, "main.go"): {false, true}, 978 filepath.Join(wd, "this_file_does_not_exist.thing"): {false, true}, 979 dn: {false, true}, 980 } 981 982 if runtime.GOOS == "windows" { 983 // This test doesn't work on Microsoft Windows because 984 // of the differences in how file permissions are 985 // implemented. For this to work, the directory where 986 // the directory exists should be inaccessible. 987 delete(tests, dn) 988 } 989 990 for f, want := range tests { 991 got, err := IsDir(f) 992 if err != nil && !want.err { 993 t.Fatalf("expected no error, got %v", err) 994 } 995 996 if got != want.exists { 997 t.Fatalf("expected %t for %s, got %t", want.exists, f, got) 998 } 999 } 1000 } 1001 1002 func TestIsNonEmptyDir(t *testing.T) { 1003 wd, err := os.Getwd() 1004 if err != nil { 1005 t.Fatal(err) 1006 } 1007 1008 h := test.NewHelper(t) 1009 defer h.Cleanup() 1010 1011 h.TempDir("empty") 1012 1013 testCases := []struct { 1014 path string 1015 empty bool 1016 err bool 1017 }{ 1018 {wd, true, false}, 1019 {"testdata", true, false}, 1020 {filepath.Join(wd, "fs.go"), false, true}, 1021 {filepath.Join(wd, "this_file_does_not_exist.thing"), false, false}, 1022 {h.Path("empty"), false, false}, 1023 } 1024 1025 // This test case doesn't work on Microsoft Windows because of the 1026 // differences in how file permissions are implemented. 1027 if runtime.GOOS != "windows" { 1028 var inaccessibleDir string 1029 cleanup := setupInaccessibleDir(t, func(dir string) error { 1030 inaccessibleDir = filepath.Join(dir, "empty") 1031 return os.Mkdir(inaccessibleDir, 0777) 1032 }) 1033 defer cleanup() 1034 1035 testCases = append(testCases, struct { 1036 path string 1037 empty bool 1038 err bool 1039 }{inaccessibleDir, false, true}) 1040 } 1041 1042 for _, want := range testCases { 1043 got, err := IsNonEmptyDir(want.path) 1044 if want.err && err == nil { 1045 if got { 1046 t.Fatalf("wanted false with error for %v, but got true", want.path) 1047 } 1048 t.Fatalf("wanted an error for %v, but it was nil", want.path) 1049 } 1050 1051 if got != want.empty { 1052 t.Fatalf("wanted %t for %v, but got %t", want.empty, want.path, got) 1053 } 1054 } 1055 } 1056 1057 func TestIsSymlink(t *testing.T) { 1058 dir, err := ioutil.TempDir("", "dep") 1059 if err != nil { 1060 t.Fatal(err) 1061 } 1062 defer os.RemoveAll(dir) 1063 1064 dirPath := filepath.Join(dir, "directory") 1065 if err = os.MkdirAll(dirPath, 0777); err != nil { 1066 t.Fatal(err) 1067 } 1068 1069 filePath := filepath.Join(dir, "file") 1070 f, err := os.Create(filePath) 1071 if err != nil { 1072 t.Fatal(err) 1073 } 1074 f.Close() 1075 1076 dirSymlink := filepath.Join(dir, "dirSymlink") 1077 fileSymlink := filepath.Join(dir, "fileSymlink") 1078 1079 if err = os.Symlink(dirPath, dirSymlink); err != nil { 1080 t.Fatal(err) 1081 } 1082 if err = os.Symlink(filePath, fileSymlink); err != nil { 1083 t.Fatal(err) 1084 } 1085 1086 var ( 1087 inaccessibleFile string 1088 inaccessibleSymlink string 1089 ) 1090 1091 cleanup := setupInaccessibleDir(t, func(dir string) error { 1092 inaccessibleFile = filepath.Join(dir, "file") 1093 if fh, err := os.Create(inaccessibleFile); err != nil { 1094 return err 1095 } else if err = fh.Close(); err != nil { 1096 return err 1097 } 1098 1099 inaccessibleSymlink = filepath.Join(dir, "symlink") 1100 return os.Symlink(inaccessibleFile, inaccessibleSymlink) 1101 }) 1102 defer cleanup() 1103 1104 tests := map[string]struct{ expected, err bool }{ 1105 dirPath: {false, false}, 1106 filePath: {false, false}, 1107 dirSymlink: {true, false}, 1108 fileSymlink: {true, false}, 1109 inaccessibleFile: {false, true}, 1110 inaccessibleSymlink: {false, true}, 1111 } 1112 1113 if runtime.GOOS == "windows" { 1114 // XXX: setting permissions works differently in Windows. Skipping 1115 // these cases until a compatible implementation is provided. 1116 delete(tests, inaccessibleFile) 1117 delete(tests, inaccessibleSymlink) 1118 } 1119 1120 for path, want := range tests { 1121 got, err := IsSymlink(path) 1122 if err != nil { 1123 if !want.err { 1124 t.Errorf("expected no error, got %v", err) 1125 } 1126 } 1127 1128 if got != want.expected { 1129 t.Errorf("expected %t for %s, got %t", want.expected, path, got) 1130 } 1131 } 1132 }