github.com/ttys3/engine@v17.12.1-ce-rc2+incompatible/pkg/archive/copy_unix_test.go (about) 1 // +build !windows 2 3 // TODO Windows: Some of these tests may be salvageable and portable to Windows. 4 5 package archive 6 7 import ( 8 "bytes" 9 "crypto/sha256" 10 "encoding/hex" 11 "fmt" 12 "io" 13 "io/ioutil" 14 "os" 15 "path/filepath" 16 "strings" 17 "testing" 18 19 "github.com/stretchr/testify/require" 20 ) 21 22 func removeAllPaths(paths ...string) { 23 for _, path := range paths { 24 os.RemoveAll(path) 25 } 26 } 27 28 func getTestTempDirs(t *testing.T) (tmpDirA, tmpDirB string) { 29 var err error 30 31 tmpDirA, err = ioutil.TempDir("", "archive-copy-test") 32 require.NoError(t, err) 33 34 tmpDirB, err = ioutil.TempDir("", "archive-copy-test") 35 require.NoError(t, err) 36 37 return 38 } 39 40 func isNotDir(err error) bool { 41 return strings.Contains(err.Error(), "not a directory") 42 } 43 44 func joinTrailingSep(pathElements ...string) string { 45 joined := filepath.Join(pathElements...) 46 47 return fmt.Sprintf("%s%c", joined, filepath.Separator) 48 } 49 50 func fileContentsEqual(t *testing.T, filenameA, filenameB string) (err error) { 51 t.Logf("checking for equal file contents: %q and %q\n", filenameA, filenameB) 52 53 fileA, err := os.Open(filenameA) 54 if err != nil { 55 return 56 } 57 defer fileA.Close() 58 59 fileB, err := os.Open(filenameB) 60 if err != nil { 61 return 62 } 63 defer fileB.Close() 64 65 hasher := sha256.New() 66 67 if _, err = io.Copy(hasher, fileA); err != nil { 68 return 69 } 70 71 hashA := hasher.Sum(nil) 72 hasher.Reset() 73 74 if _, err = io.Copy(hasher, fileB); err != nil { 75 return 76 } 77 78 hashB := hasher.Sum(nil) 79 80 if !bytes.Equal(hashA, hashB) { 81 err = fmt.Errorf("file content hashes not equal - expected %s, got %s", hex.EncodeToString(hashA), hex.EncodeToString(hashB)) 82 } 83 84 return 85 } 86 87 func dirContentsEqual(t *testing.T, newDir, oldDir string) (err error) { 88 t.Logf("checking for equal directory contents: %q and %q\n", newDir, oldDir) 89 90 var changes []Change 91 92 if changes, err = ChangesDirs(newDir, oldDir); err != nil { 93 return 94 } 95 96 if len(changes) != 0 { 97 err = fmt.Errorf("expected no changes between directories, but got: %v", changes) 98 } 99 100 return 101 } 102 103 func logDirContents(t *testing.T, dirPath string) { 104 logWalkedPaths := filepath.WalkFunc(func(path string, info os.FileInfo, err error) error { 105 if err != nil { 106 t.Errorf("stat error for path %q: %s", path, err) 107 return nil 108 } 109 110 if info.IsDir() { 111 path = joinTrailingSep(path) 112 } 113 114 t.Logf("\t%s", path) 115 116 return nil 117 }) 118 119 t.Logf("logging directory contents: %q", dirPath) 120 121 err := filepath.Walk(dirPath, logWalkedPaths) 122 require.NoError(t, err) 123 } 124 125 func testCopyHelper(t *testing.T, srcPath, dstPath string) (err error) { 126 t.Logf("copying from %q to %q (not follow symbol link)", srcPath, dstPath) 127 128 return CopyResource(srcPath, dstPath, false) 129 } 130 131 func testCopyHelperFSym(t *testing.T, srcPath, dstPath string) (err error) { 132 t.Logf("copying from %q to %q (follow symbol link)", srcPath, dstPath) 133 134 return CopyResource(srcPath, dstPath, true) 135 } 136 137 // Basic assumptions about SRC and DST: 138 // 1. SRC must exist. 139 // 2. If SRC ends with a trailing separator, it must be a directory. 140 // 3. DST parent directory must exist. 141 // 4. If DST exists as a file, it must not end with a trailing separator. 142 143 // First get these easy error cases out of the way. 144 145 // Test for error when SRC does not exist. 146 func TestCopyErrSrcNotExists(t *testing.T) { 147 tmpDirA, tmpDirB := getTestTempDirs(t) 148 defer removeAllPaths(tmpDirA, tmpDirB) 149 150 if _, err := CopyInfoSourcePath(filepath.Join(tmpDirA, "file1"), false); !os.IsNotExist(err) { 151 t.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 152 } 153 } 154 155 // Test for error when SRC ends in a trailing 156 // path separator but it exists as a file. 157 func TestCopyErrSrcNotDir(t *testing.T) { 158 tmpDirA, tmpDirB := getTestTempDirs(t) 159 defer removeAllPaths(tmpDirA, tmpDirB) 160 161 // Load A with some sample files and directories. 162 createSampleDir(t, tmpDirA) 163 164 if _, err := CopyInfoSourcePath(joinTrailingSep(tmpDirA, "file1"), false); !isNotDir(err) { 165 t.Fatalf("expected IsNotDir error, but got %T: %s", err, err) 166 } 167 } 168 169 // Test for error when SRC is a valid file or directory, 170 // but the DST parent directory does not exist. 171 func TestCopyErrDstParentNotExists(t *testing.T) { 172 tmpDirA, tmpDirB := getTestTempDirs(t) 173 defer removeAllPaths(tmpDirA, tmpDirB) 174 175 // Load A with some sample files and directories. 176 createSampleDir(t, tmpDirA) 177 178 srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false} 179 180 // Try with a file source. 181 content, err := TarResource(srcInfo) 182 if err != nil { 183 t.Fatalf("unexpected error %T: %s", err, err) 184 } 185 defer content.Close() 186 187 // Copy to a file whose parent does not exist. 188 if err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, "fakeParentDir", "file1")); err == nil { 189 t.Fatal("expected IsNotExist error, but got nil instead") 190 } 191 192 if !os.IsNotExist(err) { 193 t.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 194 } 195 196 // Try with a directory source. 197 srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true} 198 199 content, err = TarResource(srcInfo) 200 if err != nil { 201 t.Fatalf("unexpected error %T: %s", err, err) 202 } 203 defer content.Close() 204 205 // Copy to a directory whose parent does not exist. 206 if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "fakeParentDir", "fakeDstDir")); err == nil { 207 t.Fatal("expected IsNotExist error, but got nil instead") 208 } 209 210 if !os.IsNotExist(err) { 211 t.Fatalf("expected IsNotExist error, but got %T: %s", err, err) 212 } 213 } 214 215 // Test for error when DST ends in a trailing 216 // path separator but exists as a file. 217 func TestCopyErrDstNotDir(t *testing.T) { 218 tmpDirA, tmpDirB := getTestTempDirs(t) 219 defer removeAllPaths(tmpDirA, tmpDirB) 220 221 // Load A and B with some sample files and directories. 222 createSampleDir(t, tmpDirA) 223 createSampleDir(t, tmpDirB) 224 225 // Try with a file source. 226 srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false} 227 228 content, err := TarResource(srcInfo) 229 if err != nil { 230 t.Fatalf("unexpected error %T: %s", err, err) 231 } 232 defer content.Close() 233 234 if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil { 235 t.Fatal("expected IsNotDir error, but got nil instead") 236 } 237 238 if !isNotDir(err) { 239 t.Fatalf("expected IsNotDir error, but got %T: %s", err, err) 240 } 241 242 // Try with a directory source. 243 srcInfo = CopyInfo{Path: filepath.Join(tmpDirA, "dir1"), Exists: true, IsDir: true} 244 245 content, err = TarResource(srcInfo) 246 if err != nil { 247 t.Fatalf("unexpected error %T: %s", err, err) 248 } 249 defer content.Close() 250 251 if err = CopyTo(content, srcInfo, joinTrailingSep(tmpDirB, "file1")); err == nil { 252 t.Fatal("expected IsNotDir error, but got nil instead") 253 } 254 255 if !isNotDir(err) { 256 t.Fatalf("expected IsNotDir error, but got %T: %s", err, err) 257 } 258 } 259 260 // Possibilities are reduced to the remaining 10 cases: 261 // 262 // case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action 263 // =================================================================================================== 264 // A | no | - | no | - | no | create file 265 // B | no | - | no | - | yes | error 266 // C | no | - | yes | no | - | overwrite file 267 // D | no | - | yes | yes | - | create file in dst dir 268 // E | yes | no | no | - | - | create dir, copy contents 269 // F | yes | no | yes | no | - | error 270 // G | yes | no | yes | yes | - | copy dir and contents 271 // H | yes | yes | no | - | - | create dir, copy contents 272 // I | yes | yes | yes | no | - | error 273 // J | yes | yes | yes | yes | - | copy dir contents 274 // 275 276 // A. SRC specifies a file and DST (no trailing path separator) doesn't 277 // exist. This should create a file with the name DST and copy the 278 // contents of the source file into it. 279 func TestCopyCaseA(t *testing.T) { 280 tmpDirA, tmpDirB := getTestTempDirs(t) 281 defer removeAllPaths(tmpDirA, tmpDirB) 282 283 // Load A with some sample files and directories. 284 createSampleDir(t, tmpDirA) 285 286 srcPath := filepath.Join(tmpDirA, "file1") 287 dstPath := filepath.Join(tmpDirB, "itWorks.txt") 288 289 var err error 290 291 if err = testCopyHelper(t, srcPath, dstPath); err != nil { 292 t.Fatalf("unexpected error %T: %s", err, err) 293 } 294 295 err = fileContentsEqual(t, srcPath, dstPath) 296 require.NoError(t, err) 297 os.Remove(dstPath) 298 299 symlinkPath := filepath.Join(tmpDirA, "symlink3") 300 symlinkPath1 := filepath.Join(tmpDirA, "symlink4") 301 linkTarget := filepath.Join(tmpDirA, "file1") 302 303 if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil { 304 t.Fatalf("unexpected error %T: %s", err, err) 305 } 306 307 err = fileContentsEqual(t, linkTarget, dstPath) 308 require.NoError(t, err) 309 os.Remove(dstPath) 310 if err = testCopyHelperFSym(t, symlinkPath1, dstPath); err != nil { 311 t.Fatalf("unexpected error %T: %s", err, err) 312 } 313 314 err = fileContentsEqual(t, linkTarget, dstPath) 315 require.NoError(t, err) 316 } 317 318 // B. SRC specifies a file and DST (with trailing path separator) doesn't 319 // exist. This should cause an error because the copy operation cannot 320 // create a directory when copying a single file. 321 func TestCopyCaseB(t *testing.T) { 322 tmpDirA, tmpDirB := getTestTempDirs(t) 323 defer removeAllPaths(tmpDirA, tmpDirB) 324 325 // Load A with some sample files and directories. 326 createSampleDir(t, tmpDirA) 327 328 srcPath := filepath.Join(tmpDirA, "file1") 329 dstDir := joinTrailingSep(tmpDirB, "testDir") 330 331 var err error 332 333 if err = testCopyHelper(t, srcPath, dstDir); err == nil { 334 t.Fatal("expected ErrDirNotExists error, but got nil instead") 335 } 336 337 if err != ErrDirNotExists { 338 t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err) 339 } 340 341 symlinkPath := filepath.Join(tmpDirA, "symlink3") 342 343 if err = testCopyHelperFSym(t, symlinkPath, dstDir); err == nil { 344 t.Fatal("expected ErrDirNotExists error, but got nil instead") 345 } 346 if err != ErrDirNotExists { 347 t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err) 348 } 349 350 } 351 352 // C. SRC specifies a file and DST exists as a file. This should overwrite 353 // the file at DST with the contents of the source file. 354 func TestCopyCaseC(t *testing.T) { 355 tmpDirA, tmpDirB := getTestTempDirs(t) 356 defer removeAllPaths(tmpDirA, tmpDirB) 357 358 // Load A and B with some sample files and directories. 359 createSampleDir(t, tmpDirA) 360 createSampleDir(t, tmpDirB) 361 362 srcPath := filepath.Join(tmpDirA, "file1") 363 dstPath := filepath.Join(tmpDirB, "file2") 364 365 var err error 366 367 // Ensure they start out different. 368 if err = fileContentsEqual(t, srcPath, dstPath); err == nil { 369 t.Fatal("expected different file contents") 370 } 371 372 if err = testCopyHelper(t, srcPath, dstPath); err != nil { 373 t.Fatalf("unexpected error %T: %s", err, err) 374 } 375 376 err = fileContentsEqual(t, srcPath, dstPath) 377 require.NoError(t, err) 378 } 379 380 // C. Symbol link following version: 381 // SRC specifies a file and DST exists as a file. This should overwrite 382 // the file at DST with the contents of the source file. 383 func TestCopyCaseCFSym(t *testing.T) { 384 tmpDirA, tmpDirB := getTestTempDirs(t) 385 defer removeAllPaths(tmpDirA, tmpDirB) 386 387 // Load A and B with some sample files and directories. 388 createSampleDir(t, tmpDirA) 389 createSampleDir(t, tmpDirB) 390 391 symlinkPathBad := filepath.Join(tmpDirA, "symlink1") 392 symlinkPath := filepath.Join(tmpDirA, "symlink3") 393 linkTarget := filepath.Join(tmpDirA, "file1") 394 dstPath := filepath.Join(tmpDirB, "file2") 395 396 var err error 397 398 // first to test broken link 399 if err = testCopyHelperFSym(t, symlinkPathBad, dstPath); err == nil { 400 t.Fatalf("unexpected error %T: %s", err, err) 401 } 402 403 // test symbol link -> symbol link -> target 404 // Ensure they start out different. 405 if err = fileContentsEqual(t, linkTarget, dstPath); err == nil { 406 t.Fatal("expected different file contents") 407 } 408 409 if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil { 410 t.Fatalf("unexpected error %T: %s", err, err) 411 } 412 413 err = fileContentsEqual(t, linkTarget, dstPath) 414 require.NoError(t, err) 415 } 416 417 // D. SRC specifies a file and DST exists as a directory. This should place 418 // a copy of the source file inside it using the basename from SRC. Ensure 419 // this works whether DST has a trailing path separator or not. 420 func TestCopyCaseD(t *testing.T) { 421 tmpDirA, tmpDirB := getTestTempDirs(t) 422 defer removeAllPaths(tmpDirA, tmpDirB) 423 424 // Load A and B with some sample files and directories. 425 createSampleDir(t, tmpDirA) 426 createSampleDir(t, tmpDirB) 427 428 srcPath := filepath.Join(tmpDirA, "file1") 429 dstDir := filepath.Join(tmpDirB, "dir1") 430 dstPath := filepath.Join(dstDir, "file1") 431 432 var err error 433 434 // Ensure that dstPath doesn't exist. 435 if _, err = os.Stat(dstPath); !os.IsNotExist(err) { 436 t.Fatalf("did not expect dstPath %q to exist", dstPath) 437 } 438 439 if err = testCopyHelper(t, srcPath, dstDir); err != nil { 440 t.Fatalf("unexpected error %T: %s", err, err) 441 } 442 443 err = fileContentsEqual(t, srcPath, dstPath) 444 require.NoError(t, err) 445 446 // Now try again but using a trailing path separator for dstDir. 447 448 if err = os.RemoveAll(dstDir); err != nil { 449 t.Fatalf("unable to remove dstDir: %s", err) 450 } 451 452 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 453 t.Fatalf("unable to make dstDir: %s", err) 454 } 455 456 dstDir = joinTrailingSep(tmpDirB, "dir1") 457 458 if err = testCopyHelper(t, srcPath, dstDir); err != nil { 459 t.Fatalf("unexpected error %T: %s", err, err) 460 } 461 462 err = fileContentsEqual(t, srcPath, dstPath) 463 require.NoError(t, err) 464 } 465 466 // D. Symbol link following version: 467 // SRC specifies a file and DST exists as a directory. This should place 468 // a copy of the source file inside it using the basename from SRC. Ensure 469 // this works whether DST has a trailing path separator or not. 470 func TestCopyCaseDFSym(t *testing.T) { 471 tmpDirA, tmpDirB := getTestTempDirs(t) 472 defer removeAllPaths(tmpDirA, tmpDirB) 473 474 // Load A and B with some sample files and directories. 475 createSampleDir(t, tmpDirA) 476 createSampleDir(t, tmpDirB) 477 478 srcPath := filepath.Join(tmpDirA, "symlink4") 479 linkTarget := filepath.Join(tmpDirA, "file1") 480 dstDir := filepath.Join(tmpDirB, "dir1") 481 dstPath := filepath.Join(dstDir, "symlink4") 482 483 var err error 484 485 // Ensure that dstPath doesn't exist. 486 if _, err = os.Stat(dstPath); !os.IsNotExist(err) { 487 t.Fatalf("did not expect dstPath %q to exist", dstPath) 488 } 489 490 if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil { 491 t.Fatalf("unexpected error %T: %s", err, err) 492 } 493 494 err = fileContentsEqual(t, linkTarget, dstPath) 495 require.NoError(t, err) 496 497 // Now try again but using a trailing path separator for dstDir. 498 499 if err = os.RemoveAll(dstDir); err != nil { 500 t.Fatalf("unable to remove dstDir: %s", err) 501 } 502 503 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 504 t.Fatalf("unable to make dstDir: %s", err) 505 } 506 507 dstDir = joinTrailingSep(tmpDirB, "dir1") 508 509 if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil { 510 t.Fatalf("unexpected error %T: %s", err, err) 511 } 512 513 err = fileContentsEqual(t, linkTarget, dstPath) 514 require.NoError(t, err) 515 } 516 517 // E. SRC specifies a directory and DST does not exist. This should create a 518 // directory at DST and copy the contents of the SRC directory into the DST 519 // directory. Ensure this works whether DST has a trailing path separator or 520 // not. 521 func TestCopyCaseE(t *testing.T) { 522 tmpDirA, tmpDirB := getTestTempDirs(t) 523 defer removeAllPaths(tmpDirA, tmpDirB) 524 525 // Load A with some sample files and directories. 526 createSampleDir(t, tmpDirA) 527 528 srcDir := filepath.Join(tmpDirA, "dir1") 529 dstDir := filepath.Join(tmpDirB, "testDir") 530 531 var err error 532 533 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 534 t.Fatalf("unexpected error %T: %s", err, err) 535 } 536 537 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 538 t.Log("dir contents not equal") 539 logDirContents(t, tmpDirA) 540 logDirContents(t, tmpDirB) 541 t.Fatal(err) 542 } 543 544 // Now try again but using a trailing path separator for dstDir. 545 546 if err = os.RemoveAll(dstDir); err != nil { 547 t.Fatalf("unable to remove dstDir: %s", err) 548 } 549 550 dstDir = joinTrailingSep(tmpDirB, "testDir") 551 552 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 553 t.Fatalf("unexpected error %T: %s", err, err) 554 } 555 556 err = dirContentsEqual(t, dstDir, srcDir) 557 require.NoError(t, err) 558 } 559 560 // E. Symbol link following version: 561 // SRC specifies a directory and DST does not exist. This should create a 562 // directory at DST and copy the contents of the SRC directory into the DST 563 // directory. Ensure this works whether DST has a trailing path separator or 564 // not. 565 func TestCopyCaseEFSym(t *testing.T) { 566 tmpDirA, tmpDirB := getTestTempDirs(t) 567 defer removeAllPaths(tmpDirA, tmpDirB) 568 569 // Load A with some sample files and directories. 570 createSampleDir(t, tmpDirA) 571 572 srcDir := filepath.Join(tmpDirA, "dirSymlink") 573 linkTarget := filepath.Join(tmpDirA, "dir1") 574 dstDir := filepath.Join(tmpDirB, "testDir") 575 576 var err error 577 578 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 579 t.Fatalf("unexpected error %T: %s", err, err) 580 } 581 582 if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { 583 t.Log("dir contents not equal") 584 logDirContents(t, tmpDirA) 585 logDirContents(t, tmpDirB) 586 t.Fatal(err) 587 } 588 589 // Now try again but using a trailing path separator for dstDir. 590 591 if err = os.RemoveAll(dstDir); err != nil { 592 t.Fatalf("unable to remove dstDir: %s", err) 593 } 594 595 dstDir = joinTrailingSep(tmpDirB, "testDir") 596 597 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 598 t.Fatalf("unexpected error %T: %s", err, err) 599 } 600 601 err = dirContentsEqual(t, dstDir, linkTarget) 602 require.NoError(t, err) 603 } 604 605 // F. SRC specifies a directory and DST exists as a file. This should cause an 606 // error as it is not possible to overwrite a file with a directory. 607 func TestCopyCaseF(t *testing.T) { 608 tmpDirA, tmpDirB := getTestTempDirs(t) 609 defer removeAllPaths(tmpDirA, tmpDirB) 610 611 // Load A and B with some sample files and directories. 612 createSampleDir(t, tmpDirA) 613 createSampleDir(t, tmpDirB) 614 615 srcDir := filepath.Join(tmpDirA, "dir1") 616 symSrcDir := filepath.Join(tmpDirA, "dirSymlink") 617 dstFile := filepath.Join(tmpDirB, "file1") 618 619 var err error 620 621 if err = testCopyHelper(t, srcDir, dstFile); err == nil { 622 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 623 } 624 625 if err != ErrCannotCopyDir { 626 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 627 } 628 629 // now test with symbol link 630 if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil { 631 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 632 } 633 634 if err != ErrCannotCopyDir { 635 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 636 } 637 } 638 639 // G. SRC specifies a directory and DST exists as a directory. This should copy 640 // the SRC directory and all its contents to the DST directory. Ensure this 641 // works whether DST has a trailing path separator or not. 642 func TestCopyCaseG(t *testing.T) { 643 tmpDirA, tmpDirB := getTestTempDirs(t) 644 defer removeAllPaths(tmpDirA, tmpDirB) 645 646 // Load A and B with some sample files and directories. 647 createSampleDir(t, tmpDirA) 648 createSampleDir(t, tmpDirB) 649 650 srcDir := filepath.Join(tmpDirA, "dir1") 651 dstDir := filepath.Join(tmpDirB, "dir2") 652 resultDir := filepath.Join(dstDir, "dir1") 653 654 var err error 655 656 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 657 t.Fatalf("unexpected error %T: %s", err, err) 658 } 659 660 err = dirContentsEqual(t, resultDir, srcDir) 661 require.NoError(t, err) 662 663 // Now try again but using a trailing path separator for dstDir. 664 665 if err = os.RemoveAll(dstDir); err != nil { 666 t.Fatalf("unable to remove dstDir: %s", err) 667 } 668 669 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 670 t.Fatalf("unable to make dstDir: %s", err) 671 } 672 673 dstDir = joinTrailingSep(tmpDirB, "dir2") 674 675 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 676 t.Fatalf("unexpected error %T: %s", err, err) 677 } 678 679 err = dirContentsEqual(t, resultDir, srcDir) 680 require.NoError(t, err) 681 } 682 683 // G. Symbol link version: 684 // SRC specifies a directory and DST exists as a directory. This should copy 685 // the SRC directory and all its contents to the DST directory. Ensure this 686 // works whether DST has a trailing path separator or not. 687 func TestCopyCaseGFSym(t *testing.T) { 688 tmpDirA, tmpDirB := getTestTempDirs(t) 689 defer removeAllPaths(tmpDirA, tmpDirB) 690 691 // Load A and B with some sample files and directories. 692 createSampleDir(t, tmpDirA) 693 createSampleDir(t, tmpDirB) 694 695 srcDir := filepath.Join(tmpDirA, "dirSymlink") 696 linkTarget := filepath.Join(tmpDirA, "dir1") 697 dstDir := filepath.Join(tmpDirB, "dir2") 698 resultDir := filepath.Join(dstDir, "dirSymlink") 699 700 var err error 701 702 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 703 t.Fatalf("unexpected error %T: %s", err, err) 704 } 705 706 err = dirContentsEqual(t, resultDir, linkTarget) 707 require.NoError(t, err) 708 709 // Now try again but using a trailing path separator for dstDir. 710 711 if err = os.RemoveAll(dstDir); err != nil { 712 t.Fatalf("unable to remove dstDir: %s", err) 713 } 714 715 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 716 t.Fatalf("unable to make dstDir: %s", err) 717 } 718 719 dstDir = joinTrailingSep(tmpDirB, "dir2") 720 721 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 722 t.Fatalf("unexpected error %T: %s", err, err) 723 } 724 725 err = dirContentsEqual(t, resultDir, linkTarget) 726 require.NoError(t, err) 727 } 728 729 // H. SRC specifies a directory's contents only and DST does not exist. This 730 // should create a directory at DST and copy the contents of the SRC 731 // directory (but not the directory itself) into the DST directory. Ensure 732 // this works whether DST has a trailing path separator or not. 733 func TestCopyCaseH(t *testing.T) { 734 tmpDirA, tmpDirB := getTestTempDirs(t) 735 defer removeAllPaths(tmpDirA, tmpDirB) 736 737 // Load A with some sample files and directories. 738 createSampleDir(t, tmpDirA) 739 740 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 741 dstDir := filepath.Join(tmpDirB, "testDir") 742 743 var err error 744 745 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 746 t.Fatalf("unexpected error %T: %s", err, err) 747 } 748 749 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 750 t.Log("dir contents not equal") 751 logDirContents(t, tmpDirA) 752 logDirContents(t, tmpDirB) 753 t.Fatal(err) 754 } 755 756 // Now try again but using a trailing path separator for dstDir. 757 758 if err = os.RemoveAll(dstDir); err != nil { 759 t.Fatalf("unable to remove dstDir: %s", err) 760 } 761 762 dstDir = joinTrailingSep(tmpDirB, "testDir") 763 764 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 765 t.Fatalf("unexpected error %T: %s", err, err) 766 } 767 768 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 769 t.Log("dir contents not equal") 770 logDirContents(t, tmpDirA) 771 logDirContents(t, tmpDirB) 772 t.Fatal(err) 773 } 774 } 775 776 // H. Symbol link following version: 777 // SRC specifies a directory's contents only and DST does not exist. This 778 // should create a directory at DST and copy the contents of the SRC 779 // directory (but not the directory itself) into the DST directory. Ensure 780 // this works whether DST has a trailing path separator or not. 781 func TestCopyCaseHFSym(t *testing.T) { 782 tmpDirA, tmpDirB := getTestTempDirs(t) 783 defer removeAllPaths(tmpDirA, tmpDirB) 784 785 // Load A with some sample files and directories. 786 createSampleDir(t, tmpDirA) 787 788 srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "." 789 linkTarget := filepath.Join(tmpDirA, "dir1") 790 dstDir := filepath.Join(tmpDirB, "testDir") 791 792 var err error 793 794 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 795 t.Fatalf("unexpected error %T: %s", err, err) 796 } 797 798 if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { 799 t.Log("dir contents not equal") 800 logDirContents(t, tmpDirA) 801 logDirContents(t, tmpDirB) 802 t.Fatal(err) 803 } 804 805 // Now try again but using a trailing path separator for dstDir. 806 807 if err = os.RemoveAll(dstDir); err != nil { 808 t.Fatalf("unable to remove dstDir: %s", err) 809 } 810 811 dstDir = joinTrailingSep(tmpDirB, "testDir") 812 813 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 814 t.Fatalf("unexpected error %T: %s", err, err) 815 } 816 817 if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { 818 t.Log("dir contents not equal") 819 logDirContents(t, tmpDirA) 820 logDirContents(t, tmpDirB) 821 t.Fatal(err) 822 } 823 } 824 825 // I. SRC specifies a directory's contents only and DST exists as a file. This 826 // should cause an error as it is not possible to overwrite a file with a 827 // directory. 828 func TestCopyCaseI(t *testing.T) { 829 tmpDirA, tmpDirB := getTestTempDirs(t) 830 defer removeAllPaths(tmpDirA, tmpDirB) 831 832 // Load A and B with some sample files and directories. 833 createSampleDir(t, tmpDirA) 834 createSampleDir(t, tmpDirB) 835 836 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 837 symSrcDir := filepath.Join(tmpDirB, "dirSymlink") 838 dstFile := filepath.Join(tmpDirB, "file1") 839 840 var err error 841 842 if err = testCopyHelper(t, srcDir, dstFile); err == nil { 843 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 844 } 845 846 if err != ErrCannotCopyDir { 847 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 848 } 849 850 // now try with symbol link of dir 851 if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil { 852 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 853 } 854 855 if err != ErrCannotCopyDir { 856 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 857 } 858 } 859 860 // J. SRC specifies a directory's contents only and DST exists as a directory. 861 // This should copy the contents of the SRC directory (but not the directory 862 // itself) into the DST directory. Ensure this works whether DST has a 863 // trailing path separator or not. 864 func TestCopyCaseJ(t *testing.T) { 865 tmpDirA, tmpDirB := getTestTempDirs(t) 866 defer removeAllPaths(tmpDirA, tmpDirB) 867 868 // Load A and B with some sample files and directories. 869 createSampleDir(t, tmpDirA) 870 createSampleDir(t, tmpDirB) 871 872 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 873 dstDir := filepath.Join(tmpDirB, "dir5") 874 875 var err error 876 877 // first to create an empty dir 878 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 879 t.Fatalf("unable to make dstDir: %s", err) 880 } 881 882 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 883 t.Fatalf("unexpected error %T: %s", err, err) 884 } 885 886 err = dirContentsEqual(t, dstDir, srcDir) 887 require.NoError(t, err) 888 889 // Now try again but using a trailing path separator for dstDir. 890 891 if err = os.RemoveAll(dstDir); err != nil { 892 t.Fatalf("unable to remove dstDir: %s", err) 893 } 894 895 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 896 t.Fatalf("unable to make dstDir: %s", err) 897 } 898 899 dstDir = joinTrailingSep(tmpDirB, "dir5") 900 901 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 902 t.Fatalf("unexpected error %T: %s", err, err) 903 } 904 905 err = dirContentsEqual(t, dstDir, srcDir) 906 require.NoError(t, err) 907 } 908 909 // J. Symbol link following version: 910 // SRC specifies a directory's contents only and DST exists as a directory. 911 // This should copy the contents of the SRC directory (but not the directory 912 // itself) into the DST directory. Ensure this works whether DST has a 913 // trailing path separator or not. 914 func TestCopyCaseJFSym(t *testing.T) { 915 tmpDirA, tmpDirB := getTestTempDirs(t) 916 defer removeAllPaths(tmpDirA, tmpDirB) 917 918 // Load A and B with some sample files and directories. 919 createSampleDir(t, tmpDirA) 920 createSampleDir(t, tmpDirB) 921 922 srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "." 923 linkTarget := filepath.Join(tmpDirA, "dir1") 924 dstDir := filepath.Join(tmpDirB, "dir5") 925 926 var err error 927 928 // first to create an empty dir 929 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 930 t.Fatalf("unable to make dstDir: %s", err) 931 } 932 933 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 934 t.Fatalf("unexpected error %T: %s", err, err) 935 } 936 937 err = dirContentsEqual(t, dstDir, linkTarget) 938 require.NoError(t, err) 939 940 // Now try again but using a trailing path separator for dstDir. 941 942 if err = os.RemoveAll(dstDir); err != nil { 943 t.Fatalf("unable to remove dstDir: %s", err) 944 } 945 946 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 947 t.Fatalf("unable to make dstDir: %s", err) 948 } 949 950 dstDir = joinTrailingSep(tmpDirB, "dir5") 951 952 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 953 t.Fatalf("unexpected error %T: %s", err, err) 954 } 955 956 err = dirContentsEqual(t, dstDir, linkTarget) 957 require.NoError(t, err) 958 }