github.com/koderover/helm@v2.17.0+incompatible/internal/third_party/dep/fs/fs_test.go (about) 1 /* 2 Copyright (c) for portions of fs_test.go are held by The Go Authors, 2016 and are provided under 3 the BSD license. 4 5 Redistribution and use in source and binary forms, with or without 6 modification, are permitted provided that the following conditions are 7 met: 8 9 * Redistributions of source code must retain the above copyright 10 notice, this list of conditions and the following disclaimer. 11 * Redistributions in binary form must reproduce the above 12 copyright notice, this list of conditions and the following disclaimer 13 in the documentation and/or other materials provided with the 14 distribution. 15 * Neither the name of Google Inc. nor the names of its 16 contributors may be used to endorse or promote products derived from 17 this software without specific prior written permission. 18 19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package fs 33 34 import ( 35 "io/ioutil" 36 "os" 37 "os/exec" 38 "os/user" 39 "path/filepath" 40 "runtime" 41 "sync" 42 "testing" 43 ) 44 45 var ( 46 mu sync.Mutex 47 ) 48 49 func TestRenameWithFallback(t *testing.T) { 50 dir, err := ioutil.TempDir("", "helm-tmp") 51 if err != nil { 52 t.Fatal(err) 53 } 54 defer os.RemoveAll(dir) 55 56 if err = RenameWithFallback(filepath.Join(dir, "does_not_exists"), filepath.Join(dir, "dst")); err == nil { 57 t.Fatal("expected an error for non existing file, but got nil") 58 } 59 60 srcpath := filepath.Join(dir, "src") 61 62 if srcf, err := os.Create(srcpath); err != nil { 63 t.Fatal(err) 64 } else { 65 srcf.Close() 66 } 67 68 if err = RenameWithFallback(srcpath, filepath.Join(dir, "dst")); err != nil { 69 t.Fatal(err) 70 } 71 72 srcpath = filepath.Join(dir, "a") 73 if err = os.MkdirAll(srcpath, 0777); err != nil { 74 t.Fatal(err) 75 } 76 77 dstpath := filepath.Join(dir, "b") 78 if err = os.MkdirAll(dstpath, 0777); err != nil { 79 t.Fatal(err) 80 } 81 82 if err = RenameWithFallback(srcpath, dstpath); err == nil { 83 t.Fatal("expected an error if dst is an existing directory, but got nil") 84 } 85 } 86 87 func TestCopyDir(t *testing.T) { 88 dir, err := ioutil.TempDir("", "helm-tmp") 89 if err != nil { 90 t.Fatal(err) 91 } 92 defer os.RemoveAll(dir) 93 94 srcdir := filepath.Join(dir, "src") 95 if err := os.MkdirAll(srcdir, 0755); err != nil { 96 t.Fatal(err) 97 } 98 99 files := []struct { 100 path string 101 contents string 102 fi os.FileInfo 103 }{ 104 {path: "myfile", contents: "hello world"}, 105 {path: filepath.Join("subdir", "file"), contents: "subdir file"}, 106 } 107 108 // Create structure indicated in 'files' 109 for i, file := range files { 110 fn := filepath.Join(srcdir, file.path) 111 dn := filepath.Dir(fn) 112 if err = os.MkdirAll(dn, 0755); err != nil { 113 t.Fatal(err) 114 } 115 116 fh, err := os.Create(fn) 117 if err != nil { 118 t.Fatal(err) 119 } 120 121 if _, err = fh.Write([]byte(file.contents)); err != nil { 122 t.Fatal(err) 123 } 124 fh.Close() 125 126 files[i].fi, err = os.Stat(fn) 127 if err != nil { 128 t.Fatal(err) 129 } 130 } 131 132 destdir := filepath.Join(dir, "dest") 133 if err := CopyDir(srcdir, destdir); err != nil { 134 t.Fatal(err) 135 } 136 137 // Compare copy against structure indicated in 'files' 138 for _, file := range files { 139 fn := filepath.Join(srcdir, file.path) 140 dn := filepath.Dir(fn) 141 dirOK, err := IsDir(dn) 142 if err != nil { 143 t.Fatal(err) 144 } 145 if !dirOK { 146 t.Fatalf("expected %s to be a directory", dn) 147 } 148 149 got, err := ioutil.ReadFile(fn) 150 if err != nil { 151 t.Fatal(err) 152 } 153 154 if file.contents != string(got) { 155 t.Fatalf("expected: %s, got: %s", file.contents, string(got)) 156 } 157 158 gotinfo, err := os.Stat(fn) 159 if err != nil { 160 t.Fatal(err) 161 } 162 163 if file.fi.Mode() != gotinfo.Mode() { 164 t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v", 165 file.path, file.fi.Mode(), fn, gotinfo.Mode()) 166 } 167 } 168 } 169 170 func TestCopyDirFail_SrcInaccessible(t *testing.T) { 171 if runtime.GOOS == "windows" { 172 // XXX: setting permissions works differently in 173 // Microsoft Windows. Skipping this this until a 174 // compatible implementation is provided. 175 t.Skip("skipping on windows") 176 } 177 178 var currentUser, err = user.Current() 179 180 if err != nil { 181 t.Fatalf("Failed to get name of current user: %s", err) 182 } 183 184 if currentUser.Name == "root" { 185 // Skipping if root, because all files are accessible 186 t.Skip("Skipping for root user") 187 } 188 189 var srcdir, dstdir string 190 191 cleanup := setupInaccessibleDir(t, func(dir string) error { 192 srcdir = filepath.Join(dir, "src") 193 return os.MkdirAll(srcdir, 0755) 194 }) 195 defer cleanup() 196 197 dir, err := ioutil.TempDir("", "helm-tmp") 198 if err != nil { 199 t.Fatal(err) 200 } 201 defer os.RemoveAll(dir) 202 203 dstdir = filepath.Join(dir, "dst") 204 if err = CopyDir(srcdir, dstdir); err == nil { 205 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 206 } 207 } 208 209 func TestCopyDirFail_DstInaccessible(t *testing.T) { 210 if runtime.GOOS == "windows" { 211 // XXX: setting permissions works differently in 212 // Microsoft Windows. Skipping this this until a 213 // compatible implementation is provided. 214 t.Skip("skipping on windows") 215 } 216 217 var currentUser, err = user.Current() 218 219 if err != nil { 220 t.Fatalf("Failed to get name of current user: %s", err) 221 } 222 223 if currentUser.Name == "root" { 224 // Skipping if root, because all files are accessible 225 t.Skip("Skipping for root user") 226 } 227 228 var srcdir, dstdir string 229 230 dir, err := ioutil.TempDir("", "helm-tmp") 231 if err != nil { 232 t.Fatal(err) 233 } 234 defer os.RemoveAll(dir) 235 236 srcdir = filepath.Join(dir, "src") 237 if err = os.MkdirAll(srcdir, 0755); err != nil { 238 t.Fatal(err) 239 } 240 241 cleanup := setupInaccessibleDir(t, func(dir string) error { 242 dstdir = filepath.Join(dir, "dst") 243 return nil 244 }) 245 defer cleanup() 246 247 if err := CopyDir(srcdir, dstdir); err == nil { 248 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 249 } 250 } 251 252 func TestCopyDirFail_SrcIsNotDir(t *testing.T) { 253 var srcdir, dstdir string 254 255 dir, err := ioutil.TempDir("", "helm-tmp") 256 if err != nil { 257 t.Fatal(err) 258 } 259 defer os.RemoveAll(dir) 260 261 srcdir = filepath.Join(dir, "src") 262 if _, err = os.Create(srcdir); err != nil { 263 t.Fatal(err) 264 } 265 266 dstdir = filepath.Join(dir, "dst") 267 268 if err = CopyDir(srcdir, dstdir); err == nil { 269 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 270 } 271 272 if err != errSrcNotDir { 273 t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errSrcNotDir, srcdir, dstdir, err) 274 } 275 276 } 277 278 func TestCopyDirFail_DstExists(t *testing.T) { 279 var srcdir, dstdir string 280 281 dir, err := ioutil.TempDir("", "helm-tmp") 282 if err != nil { 283 t.Fatal(err) 284 } 285 defer os.RemoveAll(dir) 286 287 srcdir = filepath.Join(dir, "src") 288 if err = os.MkdirAll(srcdir, 0755); err != nil { 289 t.Fatal(err) 290 } 291 292 dstdir = filepath.Join(dir, "dst") 293 if err = os.MkdirAll(dstdir, 0755); err != nil { 294 t.Fatal(err) 295 } 296 297 if err = CopyDir(srcdir, dstdir); err == nil { 298 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 299 } 300 301 if err != errDstExist { 302 t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errDstExist, srcdir, dstdir, err) 303 } 304 } 305 306 func TestCopyDirFailOpen(t *testing.T) { 307 if runtime.GOOS == "windows" { 308 // XXX: setting permissions works differently in 309 // Microsoft Windows. os.Chmod(..., 0222) below is not 310 // enough for the file to be readonly, and os.Chmod(..., 311 // 0000) returns an invalid argument error. Skipping 312 // this this until a compatible implementation is 313 // provided. 314 t.Skip("skipping on windows") 315 } 316 317 var currentUser, err = user.Current() 318 319 if err != nil { 320 t.Fatalf("Failed to get name of current user: %s", err) 321 } 322 323 if currentUser.Name == "root" { 324 // Skipping if root, because all files are accessible 325 t.Skip("Skipping for root user") 326 } 327 328 var srcdir, dstdir string 329 330 dir, err := ioutil.TempDir("", "helm-tmp") 331 if err != nil { 332 t.Fatal(err) 333 } 334 defer os.RemoveAll(dir) 335 336 srcdir = filepath.Join(dir, "src") 337 if err = os.MkdirAll(srcdir, 0755); err != nil { 338 t.Fatal(err) 339 } 340 341 srcfn := filepath.Join(srcdir, "file") 342 srcf, err := os.Create(srcfn) 343 if err != nil { 344 t.Fatal(err) 345 } 346 srcf.Close() 347 348 // setup source file so that it cannot be read 349 if err = os.Chmod(srcfn, 0222); err != nil { 350 t.Fatal(err) 351 } 352 353 dstdir = filepath.Join(dir, "dst") 354 355 if err = CopyDir(srcdir, dstdir); err == nil { 356 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 357 } 358 } 359 360 func TestCopyFile(t *testing.T) { 361 dir, err := ioutil.TempDir("", "helm-tmp") 362 if err != nil { 363 t.Fatal(err) 364 } 365 defer os.RemoveAll(dir) 366 367 srcf, err := os.Create(filepath.Join(dir, "srcfile")) 368 if err != nil { 369 t.Fatal(err) 370 } 371 372 want := "hello world" 373 if _, err := srcf.Write([]byte(want)); err != nil { 374 t.Fatal(err) 375 } 376 srcf.Close() 377 378 destf := filepath.Join(dir, "destf") 379 if err := copyFile(srcf.Name(), destf); err != nil { 380 t.Fatal(err) 381 } 382 383 got, err := ioutil.ReadFile(destf) 384 if err != nil { 385 t.Fatal(err) 386 } 387 388 if want != string(got) { 389 t.Fatalf("expected: %s, got: %s", want, string(got)) 390 } 391 392 wantinfo, err := os.Stat(srcf.Name()) 393 if err != nil { 394 t.Fatal(err) 395 } 396 397 gotinfo, err := os.Stat(destf) 398 if err != nil { 399 t.Fatal(err) 400 } 401 402 if wantinfo.Mode() != gotinfo.Mode() { 403 t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v", srcf.Name(), wantinfo.Mode(), destf, gotinfo.Mode()) 404 } 405 } 406 407 func cleanUpDir(dir string) { 408 // NOTE(mattn): It seems that sometimes git.exe is not dead 409 // when cleanUpDir() is called. But we do not know any way to wait for it. 410 if runtime.GOOS == "windows" { 411 mu.Lock() 412 exec.Command(`taskkill`, `/F`, `/IM`, `git.exe`).Run() 413 mu.Unlock() 414 } 415 if dir != "" { 416 os.RemoveAll(dir) 417 } 418 } 419 420 func TestCopyFileSymlink(t *testing.T) { 421 var tempdir, err = ioutil.TempDir("", "gotest") 422 423 if err != nil { 424 t.Fatalf("failed to create directory: %s", err) 425 } 426 427 defer cleanUpDir(tempdir) 428 429 testcases := map[string]string{ 430 filepath.Join("./testdata/symlinks/file-symlink"): filepath.Join(tempdir, "dst-file"), 431 filepath.Join("./testdata/symlinks/windows-file-symlink"): filepath.Join(tempdir, "windows-dst-file"), 432 filepath.Join("./testdata/symlinks/invalid-symlink"): filepath.Join(tempdir, "invalid-symlink"), 433 } 434 435 for symlink, dst := range testcases { 436 t.Run(symlink, func(t *testing.T) { 437 var err error 438 if err = copyFile(symlink, dst); err != nil { 439 t.Fatalf("failed to copy symlink: %s", err) 440 } 441 442 var want, got string 443 444 if runtime.GOOS == "windows" { 445 // Creating symlinks on Windows require an additional permission 446 // regular users aren't granted usually. So we copy the file 447 // content as a fall back instead of creating a real symlink. 448 srcb, err := ioutil.ReadFile(symlink) 449 if err != nil { 450 t.Fatalf("%+v", err) 451 } 452 dstb, err := ioutil.ReadFile(dst) 453 if err != nil { 454 t.Fatalf("%+v", err) 455 } 456 457 want = string(srcb) 458 got = string(dstb) 459 } else { 460 want, err = os.Readlink(symlink) 461 if err != nil { 462 t.Fatalf("%+v", err) 463 } 464 465 got, err = os.Readlink(dst) 466 if err != nil { 467 t.Fatalf("could not resolve symlink: %s", err) 468 } 469 } 470 471 if want != got { 472 t.Fatalf("resolved path is incorrect. expected %s, got %s", want, got) 473 } 474 }) 475 } 476 } 477 478 func TestCopyFileFail(t *testing.T) { 479 if runtime.GOOS == "windows" { 480 // XXX: setting permissions works differently in 481 // Microsoft Windows. Skipping this this until a 482 // compatible implementation is provided. 483 t.Skip("skipping on windows") 484 } 485 486 var currentUser, err = user.Current() 487 488 if err != nil { 489 t.Fatalf("Failed to get name of current user: %s", err) 490 } 491 492 if currentUser.Name == "root" { 493 // Skipping if root, because all files are accessible 494 t.Skip("Skipping for root user") 495 } 496 497 dir, err := ioutil.TempDir("", "helm-tmp") 498 if err != nil { 499 t.Fatal(err) 500 } 501 defer os.RemoveAll(dir) 502 503 srcf, err := os.Create(filepath.Join(dir, "srcfile")) 504 if err != nil { 505 t.Fatal(err) 506 } 507 srcf.Close() 508 509 var dstdir string 510 511 cleanup := setupInaccessibleDir(t, func(dir string) error { 512 dstdir = filepath.Join(dir, "dir") 513 return os.Mkdir(dstdir, 0777) 514 }) 515 defer cleanup() 516 517 fn := filepath.Join(dstdir, "file") 518 if err := copyFile(srcf.Name(), fn); err == nil { 519 t.Fatalf("expected error for %s, got none", fn) 520 } 521 } 522 523 // setupInaccessibleDir creates a temporary location with a single 524 // directory in it, in such a way that that directory is not accessible 525 // after this function returns. 526 // 527 // op is called with the directory as argument, so that it can create 528 // files or other test artifacts. 529 // 530 // If setupInaccessibleDir fails in its preparation, or op fails, t.Fatal 531 // will be invoked. 532 // 533 // This function returns a cleanup function that removes all the temporary 534 // files this function creates. It is the caller's responsibility to call 535 // this function before the test is done running, whether there's an error or not. 536 func setupInaccessibleDir(t *testing.T, op func(dir string) error) func() { 537 dir, err := ioutil.TempDir("", "helm-tmp") 538 if err != nil { 539 t.Fatal(err) 540 return nil // keep compiler happy 541 } 542 543 subdir := filepath.Join(dir, "dir") 544 545 cleanup := func() { 546 if err := os.Chmod(subdir, 0777); err != nil { 547 t.Error(err) 548 } 549 if err := os.RemoveAll(dir); err != nil { 550 t.Error(err) 551 } 552 } 553 554 if err := os.Mkdir(subdir, 0777); err != nil { 555 cleanup() 556 t.Fatal(err) 557 return nil 558 } 559 560 if err := op(subdir); err != nil { 561 cleanup() 562 t.Fatal(err) 563 return nil 564 } 565 566 if err := os.Chmod(subdir, 0666); err != nil { 567 cleanup() 568 t.Fatal(err) 569 return nil 570 } 571 572 return cleanup 573 } 574 575 func TestIsDir(t *testing.T) { 576 577 var currentUser, err = user.Current() 578 579 if err != nil { 580 t.Fatalf("Failed to get name of current user: %s", err) 581 } 582 583 if currentUser.Name == "root" { 584 // Skipping if root, because all files are accessible 585 t.Skip("Skipping for root user") 586 } 587 588 wd, err := os.Getwd() 589 if err != nil { 590 t.Fatal(err) 591 } 592 593 var dn string 594 595 cleanup := setupInaccessibleDir(t, func(dir string) error { 596 dn = filepath.Join(dir, "dir") 597 return os.Mkdir(dn, 0777) 598 }) 599 defer cleanup() 600 601 tests := map[string]struct { 602 exists bool 603 err bool 604 }{ 605 wd: {true, false}, 606 filepath.Join(wd, "testdata"): {true, false}, 607 filepath.Join(wd, "main.go"): {false, true}, 608 filepath.Join(wd, "this_file_does_not_exist.thing"): {false, true}, 609 dn: {false, true}, 610 } 611 612 if runtime.GOOS == "windows" { 613 // This test doesn't work on Microsoft Windows because 614 // of the differences in how file permissions are 615 // implemented. For this to work, the directory where 616 // the directory exists should be inaccessible. 617 delete(tests, dn) 618 } 619 620 for f, want := range tests { 621 got, err := IsDir(f) 622 if err != nil && !want.err { 623 t.Fatalf("expected no error, got %v", err) 624 } 625 626 if got != want.exists { 627 t.Fatalf("expected %t for %s, got %t", want.exists, f, got) 628 } 629 } 630 } 631 632 func TestIsSymlink(t *testing.T) { 633 634 var currentUser, err = user.Current() 635 636 if err != nil { 637 t.Fatalf("Failed to get name of current user: %s", err) 638 } 639 640 if currentUser.Name == "root" { 641 // Skipping if root, because all files are accessible 642 t.Skip("Skipping for root user") 643 } 644 645 dir, err := ioutil.TempDir("", "helm-tmp") 646 if err != nil { 647 t.Fatal(err) 648 } 649 defer os.RemoveAll(dir) 650 651 dirPath := filepath.Join(dir, "directory") 652 if err = os.MkdirAll(dirPath, 0777); err != nil { 653 t.Fatal(err) 654 } 655 656 filePath := filepath.Join(dir, "file") 657 f, err := os.Create(filePath) 658 if err != nil { 659 t.Fatal(err) 660 } 661 f.Close() 662 663 dirSymlink := filepath.Join(dir, "dirSymlink") 664 fileSymlink := filepath.Join(dir, "fileSymlink") 665 666 if err = os.Symlink(dirPath, dirSymlink); err != nil { 667 t.Fatal(err) 668 } 669 if err = os.Symlink(filePath, fileSymlink); err != nil { 670 t.Fatal(err) 671 } 672 673 var ( 674 inaccessibleFile string 675 inaccessibleSymlink string 676 ) 677 678 cleanup := setupInaccessibleDir(t, func(dir string) error { 679 inaccessibleFile = filepath.Join(dir, "file") 680 if fh, err := os.Create(inaccessibleFile); err != nil { 681 return err 682 } else if err = fh.Close(); err != nil { 683 return err 684 } 685 686 inaccessibleSymlink = filepath.Join(dir, "symlink") 687 return os.Symlink(inaccessibleFile, inaccessibleSymlink) 688 }) 689 defer cleanup() 690 691 tests := map[string]struct{ expected, err bool }{ 692 dirPath: {false, false}, 693 filePath: {false, false}, 694 dirSymlink: {true, false}, 695 fileSymlink: {true, false}, 696 inaccessibleFile: {false, true}, 697 inaccessibleSymlink: {false, true}, 698 } 699 700 if runtime.GOOS == "windows" { 701 // XXX: setting permissions works differently in Windows. Skipping 702 // these cases until a compatible implementation is provided. 703 delete(tests, inaccessibleFile) 704 delete(tests, inaccessibleSymlink) 705 } 706 707 for path, want := range tests { 708 got, err := IsSymlink(path) 709 if err != nil { 710 if !want.err { 711 t.Errorf("expected no error, got %v", err) 712 } 713 } 714 715 if got != want.expected { 716 t.Errorf("expected %t for %s, got %t", want.expected, path, got) 717 } 718 } 719 }