github.com/stefanmcshane/helm@v0.0.0-20221213002717-88a4a2c6e77d/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 "path/filepath" 39 "runtime" 40 "sync" 41 "testing" 42 ) 43 44 var ( 45 mu sync.Mutex 46 ) 47 48 func TestRenameWithFallback(t *testing.T) { 49 dir := t.TempDir() 50 51 if err := RenameWithFallback(filepath.Join(dir, "does_not_exists"), filepath.Join(dir, "dst")); err == nil { 52 t.Fatal("expected an error for non existing file, but got nil") 53 } 54 55 srcpath := filepath.Join(dir, "src") 56 57 if srcf, err := os.Create(srcpath); err != nil { 58 t.Fatal(err) 59 } else { 60 srcf.Close() 61 } 62 63 if err := RenameWithFallback(srcpath, filepath.Join(dir, "dst")); err != nil { 64 t.Fatal(err) 65 } 66 67 srcpath = filepath.Join(dir, "a") 68 if err := os.MkdirAll(srcpath, 0777); err != nil { 69 t.Fatal(err) 70 } 71 72 dstpath := filepath.Join(dir, "b") 73 if err := os.MkdirAll(dstpath, 0777); err != nil { 74 t.Fatal(err) 75 } 76 77 if err := RenameWithFallback(srcpath, dstpath); err == nil { 78 t.Fatal("expected an error if dst is an existing directory, but got nil") 79 } 80 } 81 82 func TestCopyDir(t *testing.T) { 83 dir := t.TempDir() 84 85 srcdir := filepath.Join(dir, "src") 86 if err := os.MkdirAll(srcdir, 0755); err != nil { 87 t.Fatal(err) 88 } 89 90 files := []struct { 91 path string 92 contents string 93 fi os.FileInfo 94 }{ 95 {path: "myfile", contents: "hello world"}, 96 {path: filepath.Join("subdir", "file"), contents: "subdir file"}, 97 } 98 99 // Create structure indicated in 'files' 100 for i, file := range files { 101 fn := filepath.Join(srcdir, file.path) 102 dn := filepath.Dir(fn) 103 if err := os.MkdirAll(dn, 0755); err != nil { 104 t.Fatal(err) 105 } 106 107 fh, err := os.Create(fn) 108 if err != nil { 109 t.Fatal(err) 110 } 111 112 if _, err = fh.Write([]byte(file.contents)); err != nil { 113 t.Fatal(err) 114 } 115 fh.Close() 116 117 files[i].fi, err = os.Stat(fn) 118 if err != nil { 119 t.Fatal(err) 120 } 121 } 122 123 destdir := filepath.Join(dir, "dest") 124 if err := CopyDir(srcdir, destdir); err != nil { 125 t.Fatal(err) 126 } 127 128 // Compare copy against structure indicated in 'files' 129 for _, file := range files { 130 fn := filepath.Join(srcdir, file.path) 131 dn := filepath.Dir(fn) 132 dirOK, err := IsDir(dn) 133 if err != nil { 134 t.Fatal(err) 135 } 136 if !dirOK { 137 t.Fatalf("expected %s to be a directory", dn) 138 } 139 140 got, err := ioutil.ReadFile(fn) 141 if err != nil { 142 t.Fatal(err) 143 } 144 145 if file.contents != string(got) { 146 t.Fatalf("expected: %s, got: %s", file.contents, string(got)) 147 } 148 149 gotinfo, err := os.Stat(fn) 150 if err != nil { 151 t.Fatal(err) 152 } 153 154 if file.fi.Mode() != gotinfo.Mode() { 155 t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v", 156 file.path, file.fi.Mode(), fn, gotinfo.Mode()) 157 } 158 } 159 } 160 161 func TestCopyDirFail_SrcInaccessible(t *testing.T) { 162 if runtime.GOOS == "windows" { 163 // XXX: setting permissions works differently in 164 // Microsoft Windows. Skipping this until a 165 // compatible implementation is provided. 166 t.Skip("skipping on windows") 167 } 168 169 var currentUID = os.Getuid() 170 171 if currentUID == 0 { 172 // Skipping if root, because all files are accessible 173 t.Skip("Skipping for root user") 174 } 175 176 var srcdir, dstdir string 177 178 cleanup := setupInaccessibleDir(t, func(dir string) error { 179 srcdir = filepath.Join(dir, "src") 180 return os.MkdirAll(srcdir, 0755) 181 }) 182 defer cleanup() 183 184 dir := t.TempDir() 185 186 dstdir = filepath.Join(dir, "dst") 187 if err := CopyDir(srcdir, dstdir); err == nil { 188 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 189 } 190 } 191 192 func TestCopyDirFail_DstInaccessible(t *testing.T) { 193 if runtime.GOOS == "windows" { 194 // XXX: setting permissions works differently in 195 // Microsoft Windows. Skipping this until a 196 // compatible implementation is provided. 197 t.Skip("skipping on windows") 198 } 199 200 var currentUID = os.Getuid() 201 202 if currentUID == 0 { 203 // Skipping if root, because all files are accessible 204 t.Skip("Skipping for root user") 205 } 206 207 var srcdir, dstdir string 208 209 dir := t.TempDir() 210 211 srcdir = filepath.Join(dir, "src") 212 if err := os.MkdirAll(srcdir, 0755); err != nil { 213 t.Fatal(err) 214 } 215 216 cleanup := setupInaccessibleDir(t, func(dir string) error { 217 dstdir = filepath.Join(dir, "dst") 218 return nil 219 }) 220 defer cleanup() 221 222 if err := CopyDir(srcdir, dstdir); err == nil { 223 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 224 } 225 } 226 227 func TestCopyDirFail_SrcIsNotDir(t *testing.T) { 228 var srcdir, dstdir string 229 var err error 230 231 dir := t.TempDir() 232 233 srcdir = filepath.Join(dir, "src") 234 if _, err = os.Create(srcdir); err != nil { 235 t.Fatal(err) 236 } 237 238 dstdir = filepath.Join(dir, "dst") 239 240 if err = CopyDir(srcdir, dstdir); err == nil { 241 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 242 } 243 244 if err != errSrcNotDir { 245 t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errSrcNotDir, srcdir, dstdir, err) 246 } 247 248 } 249 250 func TestCopyDirFail_DstExists(t *testing.T) { 251 var srcdir, dstdir string 252 var err error 253 254 dir := t.TempDir() 255 256 srcdir = filepath.Join(dir, "src") 257 if err = os.MkdirAll(srcdir, 0755); err != nil { 258 t.Fatal(err) 259 } 260 261 dstdir = filepath.Join(dir, "dst") 262 if err = os.MkdirAll(dstdir, 0755); err != nil { 263 t.Fatal(err) 264 } 265 266 if err = CopyDir(srcdir, dstdir); err == nil { 267 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 268 } 269 270 if err != errDstExist { 271 t.Fatalf("expected %v error for CopyDir(%s, %s), got %s", errDstExist, srcdir, dstdir, err) 272 } 273 } 274 275 func TestCopyDirFailOpen(t *testing.T) { 276 if runtime.GOOS == "windows" { 277 // XXX: setting permissions works differently in 278 // Microsoft Windows. os.Chmod(..., 0222) below is not 279 // enough for the file to be readonly, and os.Chmod(..., 280 // 0000) returns an invalid argument error. Skipping 281 // this until a compatible implementation is 282 // provided. 283 t.Skip("skipping on windows") 284 } 285 286 var currentUID = os.Getuid() 287 288 if currentUID == 0 { 289 // Skipping if root, because all files are accessible 290 t.Skip("Skipping for root user") 291 } 292 293 var srcdir, dstdir string 294 295 dir := t.TempDir() 296 297 srcdir = filepath.Join(dir, "src") 298 if err := os.MkdirAll(srcdir, 0755); err != nil { 299 t.Fatal(err) 300 } 301 302 srcfn := filepath.Join(srcdir, "file") 303 srcf, err := os.Create(srcfn) 304 if err != nil { 305 t.Fatal(err) 306 } 307 srcf.Close() 308 309 // setup source file so that it cannot be read 310 if err = os.Chmod(srcfn, 0222); err != nil { 311 t.Fatal(err) 312 } 313 314 dstdir = filepath.Join(dir, "dst") 315 316 if err = CopyDir(srcdir, dstdir); err == nil { 317 t.Fatalf("expected error for CopyDir(%s, %s), got none", srcdir, dstdir) 318 } 319 } 320 321 func TestCopyFile(t *testing.T) { 322 dir := t.TempDir() 323 324 srcf, err := os.Create(filepath.Join(dir, "srcfile")) 325 if err != nil { 326 t.Fatal(err) 327 } 328 329 want := "hello world" 330 if _, err := srcf.Write([]byte(want)); err != nil { 331 t.Fatal(err) 332 } 333 srcf.Close() 334 335 destf := filepath.Join(dir, "destf") 336 if err := copyFile(srcf.Name(), destf); err != nil { 337 t.Fatal(err) 338 } 339 340 got, err := ioutil.ReadFile(destf) 341 if err != nil { 342 t.Fatal(err) 343 } 344 345 if want != string(got) { 346 t.Fatalf("expected: %s, got: %s", want, string(got)) 347 } 348 349 wantinfo, err := os.Stat(srcf.Name()) 350 if err != nil { 351 t.Fatal(err) 352 } 353 354 gotinfo, err := os.Stat(destf) 355 if err != nil { 356 t.Fatal(err) 357 } 358 359 if wantinfo.Mode() != gotinfo.Mode() { 360 t.Fatalf("expected %s: %#v\n to be the same mode as %s: %#v", srcf.Name(), wantinfo.Mode(), destf, gotinfo.Mode()) 361 } 362 } 363 364 func cleanUpDir(dir string) { 365 // NOTE(mattn): It seems that sometimes git.exe is not dead 366 // when cleanUpDir() is called. But we do not know any way to wait for it. 367 if runtime.GOOS == "windows" { 368 mu.Lock() 369 exec.Command(`taskkill`, `/F`, `/IM`, `git.exe`).Run() 370 mu.Unlock() 371 } 372 if dir != "" { 373 os.RemoveAll(dir) 374 } 375 } 376 377 func TestCopyFileSymlink(t *testing.T) { 378 tempdir := t.TempDir() 379 380 testcases := map[string]string{ 381 filepath.Join("./testdata/symlinks/file-symlink"): filepath.Join(tempdir, "dst-file"), 382 filepath.Join("./testdata/symlinks/windows-file-symlink"): filepath.Join(tempdir, "windows-dst-file"), 383 filepath.Join("./testdata/symlinks/invalid-symlink"): filepath.Join(tempdir, "invalid-symlink"), 384 } 385 386 for symlink, dst := range testcases { 387 t.Run(symlink, func(t *testing.T) { 388 var err error 389 if err = copyFile(symlink, dst); err != nil { 390 t.Fatalf("failed to copy symlink: %s", err) 391 } 392 393 var want, got string 394 395 if runtime.GOOS == "windows" { 396 // Creating symlinks on Windows require an additional permission 397 // regular users aren't granted usually. So we copy the file 398 // content as a fall back instead of creating a real symlink. 399 srcb, err := ioutil.ReadFile(symlink) 400 if err != nil { 401 t.Fatalf("%+v", err) 402 } 403 dstb, err := ioutil.ReadFile(dst) 404 if err != nil { 405 t.Fatalf("%+v", err) 406 } 407 408 want = string(srcb) 409 got = string(dstb) 410 } else { 411 want, err = os.Readlink(symlink) 412 if err != nil { 413 t.Fatalf("%+v", err) 414 } 415 416 got, err = os.Readlink(dst) 417 if err != nil { 418 t.Fatalf("could not resolve symlink: %s", err) 419 } 420 } 421 422 if want != got { 423 t.Fatalf("resolved path is incorrect. expected %s, got %s", want, got) 424 } 425 }) 426 } 427 } 428 429 func TestCopyFileFail(t *testing.T) { 430 if runtime.GOOS == "windows" { 431 // XXX: setting permissions works differently in 432 // Microsoft Windows. Skipping this until a 433 // compatible implementation is provided. 434 t.Skip("skipping on windows") 435 } 436 437 var currentUID = os.Getuid() 438 439 if currentUID == 0 { 440 // Skipping if root, because all files are accessible 441 t.Skip("Skipping for root user") 442 } 443 444 dir := t.TempDir() 445 446 srcf, err := os.Create(filepath.Join(dir, "srcfile")) 447 if err != nil { 448 t.Fatal(err) 449 } 450 srcf.Close() 451 452 var dstdir string 453 454 cleanup := setupInaccessibleDir(t, func(dir string) error { 455 dstdir = filepath.Join(dir, "dir") 456 return os.Mkdir(dstdir, 0777) 457 }) 458 defer cleanup() 459 460 fn := filepath.Join(dstdir, "file") 461 if err := copyFile(srcf.Name(), fn); err == nil { 462 t.Fatalf("expected error for %s, got none", fn) 463 } 464 } 465 466 // setupInaccessibleDir creates a temporary location with a single 467 // directory in it, in such a way that directory is not accessible 468 // after this function returns. 469 // 470 // op is called with the directory as argument, so that it can create 471 // files or other test artifacts. 472 // 473 // If setupInaccessibleDir fails in its preparation, or op fails, t.Fatal 474 // will be invoked. 475 // 476 // This function returns a cleanup function that removes all the temporary 477 // files this function creates. It is the caller's responsibility to call 478 // this function before the test is done running, whether there's an error or not. 479 func setupInaccessibleDir(t *testing.T, op func(dir string) error) func() { 480 dir := t.TempDir() 481 482 subdir := filepath.Join(dir, "dir") 483 484 cleanup := func() { 485 if err := os.Chmod(subdir, 0777); err != nil { 486 t.Error(err) 487 } 488 } 489 490 if err := os.Mkdir(subdir, 0777); err != nil { 491 cleanup() 492 t.Fatal(err) 493 return nil 494 } 495 496 if err := op(subdir); err != nil { 497 cleanup() 498 t.Fatal(err) 499 return nil 500 } 501 502 if err := os.Chmod(subdir, 0666); err != nil { 503 cleanup() 504 t.Fatal(err) 505 return nil 506 } 507 508 return cleanup 509 } 510 511 func TestIsDir(t *testing.T) { 512 513 var currentUID = os.Getuid() 514 515 if currentUID == 0 { 516 // Skipping if root, because all files are accessible 517 t.Skip("Skipping for root user") 518 } 519 520 wd, err := os.Getwd() 521 if err != nil { 522 t.Fatal(err) 523 } 524 525 var dn string 526 527 cleanup := setupInaccessibleDir(t, func(dir string) error { 528 dn = filepath.Join(dir, "dir") 529 return os.Mkdir(dn, 0777) 530 }) 531 defer cleanup() 532 533 tests := map[string]struct { 534 exists bool 535 err bool 536 }{ 537 wd: {true, false}, 538 filepath.Join(wd, "testdata"): {true, false}, 539 filepath.Join(wd, "main.go"): {false, true}, 540 filepath.Join(wd, "this_file_does_not_exist.thing"): {false, true}, 541 dn: {false, true}, 542 } 543 544 if runtime.GOOS == "windows" { 545 // This test doesn't work on Microsoft Windows because 546 // of the differences in how file permissions are 547 // implemented. For this to work, the directory where 548 // the directory exists should be inaccessible. 549 delete(tests, dn) 550 } 551 552 for f, want := range tests { 553 got, err := IsDir(f) 554 if err != nil && !want.err { 555 t.Fatalf("expected no error, got %v", err) 556 } 557 558 if got != want.exists { 559 t.Fatalf("expected %t for %s, got %t", want.exists, f, got) 560 } 561 } 562 } 563 564 func TestIsSymlink(t *testing.T) { 565 566 var currentUID = os.Getuid() 567 568 if currentUID == 0 { 569 // Skipping if root, because all files are accessible 570 t.Skip("Skipping for root user") 571 } 572 573 dir := t.TempDir() 574 575 dirPath := filepath.Join(dir, "directory") 576 if err := os.MkdirAll(dirPath, 0777); err != nil { 577 t.Fatal(err) 578 } 579 580 filePath := filepath.Join(dir, "file") 581 f, err := os.Create(filePath) 582 if err != nil { 583 t.Fatal(err) 584 } 585 f.Close() 586 587 dirSymlink := filepath.Join(dir, "dirSymlink") 588 fileSymlink := filepath.Join(dir, "fileSymlink") 589 590 if err = os.Symlink(dirPath, dirSymlink); err != nil { 591 t.Fatal(err) 592 } 593 if err = os.Symlink(filePath, fileSymlink); err != nil { 594 t.Fatal(err) 595 } 596 597 var ( 598 inaccessibleFile string 599 inaccessibleSymlink string 600 ) 601 602 cleanup := setupInaccessibleDir(t, func(dir string) error { 603 inaccessibleFile = filepath.Join(dir, "file") 604 if fh, err := os.Create(inaccessibleFile); err != nil { 605 return err 606 } else if err = fh.Close(); err != nil { 607 return err 608 } 609 610 inaccessibleSymlink = filepath.Join(dir, "symlink") 611 return os.Symlink(inaccessibleFile, inaccessibleSymlink) 612 }) 613 defer cleanup() 614 615 tests := map[string]struct{ expected, err bool }{ 616 dirPath: {false, false}, 617 filePath: {false, false}, 618 dirSymlink: {true, false}, 619 fileSymlink: {true, false}, 620 inaccessibleFile: {false, true}, 621 inaccessibleSymlink: {false, true}, 622 } 623 624 if runtime.GOOS == "windows" { 625 // XXX: setting permissions works differently in Windows. Skipping 626 // these cases until a compatible implementation is provided. 627 delete(tests, inaccessibleFile) 628 delete(tests, inaccessibleSymlink) 629 } 630 631 for path, want := range tests { 632 got, err := IsSymlink(path) 633 if err != nil { 634 if !want.err { 635 t.Errorf("expected no error, got %v", err) 636 } 637 } 638 639 if got != want.expected { 640 t.Errorf("expected %t for %s, got %t", want.expected, path, got) 641 } 642 } 643 }