github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/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 // import "github.com/demonoid81/moby/pkg/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 "gotest.tools/v3/assert" 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 assert.NilError(t, err) 33 34 tmpDirB, err = ioutil.TempDir("", "archive-copy-test") 35 assert.NilError(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 assert.NilError(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 // Test to check if CopyTo works with a long (>100 characters) destination file name. 261 // This is a regression (see https://github.com/docker/for-linux/issues/484). 262 func TestCopyLongDstFilename(t *testing.T) { 263 const longName = "a_very_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx_long_filename_that_is_101_characters" 264 tmpDirA, tmpDirB := getTestTempDirs(t) 265 defer removeAllPaths(tmpDirA, tmpDirB) 266 267 // Load A with some sample files and directories. 268 createSampleDir(t, tmpDirA) 269 270 srcInfo := CopyInfo{Path: filepath.Join(tmpDirA, "file1"), Exists: true, IsDir: false} 271 272 content, err := TarResource(srcInfo) 273 if err != nil { 274 t.Fatalf("unexpected error %T: %s", err, err) 275 } 276 defer content.Close() 277 278 err = CopyTo(content, srcInfo, filepath.Join(tmpDirB, longName)) 279 if err != nil { 280 t.Fatalf("unexpected error %T: %s", err, err) 281 } 282 } 283 284 // Possibilities are reduced to the remaining 10 cases: 285 // 286 // case | srcIsDir | onlyDirContents | dstExists | dstIsDir | dstTrSep | action 287 // =================================================================================================== 288 // A | no | - | no | - | no | create file 289 // B | no | - | no | - | yes | error 290 // C | no | - | yes | no | - | overwrite file 291 // D | no | - | yes | yes | - | create file in dst dir 292 // E | yes | no | no | - | - | create dir, copy contents 293 // F | yes | no | yes | no | - | error 294 // G | yes | no | yes | yes | - | copy dir and contents 295 // H | yes | yes | no | - | - | create dir, copy contents 296 // I | yes | yes | yes | no | - | error 297 // J | yes | yes | yes | yes | - | copy dir contents 298 // 299 300 // A. SRC specifies a file and DST (no trailing path separator) doesn't 301 // exist. This should create a file with the name DST and copy the 302 // contents of the source file into it. 303 func TestCopyCaseA(t *testing.T) { 304 tmpDirA, tmpDirB := getTestTempDirs(t) 305 defer removeAllPaths(tmpDirA, tmpDirB) 306 307 // Load A with some sample files and directories. 308 createSampleDir(t, tmpDirA) 309 310 srcPath := filepath.Join(tmpDirA, "file1") 311 dstPath := filepath.Join(tmpDirB, "itWorks.txt") 312 313 var err error 314 315 if err = testCopyHelper(t, srcPath, dstPath); err != nil { 316 t.Fatalf("unexpected error %T: %s", err, err) 317 } 318 319 err = fileContentsEqual(t, srcPath, dstPath) 320 assert.NilError(t, err) 321 os.Remove(dstPath) 322 323 symlinkPath := filepath.Join(tmpDirA, "symlink3") 324 symlinkPath1 := filepath.Join(tmpDirA, "symlink4") 325 linkTarget := filepath.Join(tmpDirA, "file1") 326 327 if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil { 328 t.Fatalf("unexpected error %T: %s", err, err) 329 } 330 331 err = fileContentsEqual(t, linkTarget, dstPath) 332 assert.NilError(t, err) 333 os.Remove(dstPath) 334 if err = testCopyHelperFSym(t, symlinkPath1, dstPath); err != nil { 335 t.Fatalf("unexpected error %T: %s", err, err) 336 } 337 338 err = fileContentsEqual(t, linkTarget, dstPath) 339 assert.NilError(t, err) 340 } 341 342 // B. SRC specifies a file and DST (with trailing path separator) doesn't 343 // exist. This should cause an error because the copy operation cannot 344 // create a directory when copying a single file. 345 func TestCopyCaseB(t *testing.T) { 346 tmpDirA, tmpDirB := getTestTempDirs(t) 347 defer removeAllPaths(tmpDirA, tmpDirB) 348 349 // Load A with some sample files and directories. 350 createSampleDir(t, tmpDirA) 351 352 srcPath := filepath.Join(tmpDirA, "file1") 353 dstDir := joinTrailingSep(tmpDirB, "testDir") 354 355 var err error 356 357 if err = testCopyHelper(t, srcPath, dstDir); err == nil { 358 t.Fatal("expected ErrDirNotExists error, but got nil instead") 359 } 360 361 if err != ErrDirNotExists { 362 t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err) 363 } 364 365 symlinkPath := filepath.Join(tmpDirA, "symlink3") 366 367 if err = testCopyHelperFSym(t, symlinkPath, dstDir); err == nil { 368 t.Fatal("expected ErrDirNotExists error, but got nil instead") 369 } 370 if err != ErrDirNotExists { 371 t.Fatalf("expected ErrDirNotExists error, but got %T: %s", err, err) 372 } 373 374 } 375 376 // C. SRC specifies a file and DST exists as a file. This should overwrite 377 // the file at DST with the contents of the source file. 378 func TestCopyCaseC(t *testing.T) { 379 tmpDirA, tmpDirB := getTestTempDirs(t) 380 defer removeAllPaths(tmpDirA, tmpDirB) 381 382 // Load A and B with some sample files and directories. 383 createSampleDir(t, tmpDirA) 384 createSampleDir(t, tmpDirB) 385 386 srcPath := filepath.Join(tmpDirA, "file1") 387 dstPath := filepath.Join(tmpDirB, "file2") 388 389 var err error 390 391 // Ensure they start out different. 392 if err = fileContentsEqual(t, srcPath, dstPath); err == nil { 393 t.Fatal("expected different file contents") 394 } 395 396 if err = testCopyHelper(t, srcPath, dstPath); err != nil { 397 t.Fatalf("unexpected error %T: %s", err, err) 398 } 399 400 err = fileContentsEqual(t, srcPath, dstPath) 401 assert.NilError(t, err) 402 } 403 404 // C. Symbol link following version: 405 // SRC specifies a file and DST exists as a file. This should overwrite 406 // the file at DST with the contents of the source file. 407 func TestCopyCaseCFSym(t *testing.T) { 408 tmpDirA, tmpDirB := getTestTempDirs(t) 409 defer removeAllPaths(tmpDirA, tmpDirB) 410 411 // Load A and B with some sample files and directories. 412 createSampleDir(t, tmpDirA) 413 createSampleDir(t, tmpDirB) 414 415 symlinkPathBad := filepath.Join(tmpDirA, "symlink1") 416 symlinkPath := filepath.Join(tmpDirA, "symlink3") 417 linkTarget := filepath.Join(tmpDirA, "file1") 418 dstPath := filepath.Join(tmpDirB, "file2") 419 420 var err error 421 422 // first to test broken link 423 if err = testCopyHelperFSym(t, symlinkPathBad, dstPath); err == nil { 424 t.Fatalf("unexpected error %T: %s", err, err) 425 } 426 427 // test symbol link -> symbol link -> target 428 // Ensure they start out different. 429 if err = fileContentsEqual(t, linkTarget, dstPath); err == nil { 430 t.Fatal("expected different file contents") 431 } 432 433 if err = testCopyHelperFSym(t, symlinkPath, dstPath); err != nil { 434 t.Fatalf("unexpected error %T: %s", err, err) 435 } 436 437 err = fileContentsEqual(t, linkTarget, dstPath) 438 assert.NilError(t, err) 439 } 440 441 // D. SRC specifies a file and DST exists as a directory. This should place 442 // a copy of the source file inside it using the basename from SRC. Ensure 443 // this works whether DST has a trailing path separator or not. 444 func TestCopyCaseD(t *testing.T) { 445 tmpDirA, tmpDirB := getTestTempDirs(t) 446 defer removeAllPaths(tmpDirA, tmpDirB) 447 448 // Load A and B with some sample files and directories. 449 createSampleDir(t, tmpDirA) 450 createSampleDir(t, tmpDirB) 451 452 srcPath := filepath.Join(tmpDirA, "file1") 453 dstDir := filepath.Join(tmpDirB, "dir1") 454 dstPath := filepath.Join(dstDir, "file1") 455 456 var err error 457 458 // Ensure that dstPath doesn't exist. 459 if _, err = os.Stat(dstPath); !os.IsNotExist(err) { 460 t.Fatalf("did not expect dstPath %q to exist", dstPath) 461 } 462 463 if err = testCopyHelper(t, srcPath, dstDir); err != nil { 464 t.Fatalf("unexpected error %T: %s", err, err) 465 } 466 467 err = fileContentsEqual(t, srcPath, dstPath) 468 assert.NilError(t, err) 469 470 // Now try again but using a trailing path separator for dstDir. 471 472 if err = os.RemoveAll(dstDir); err != nil { 473 t.Fatalf("unable to remove dstDir: %s", err) 474 } 475 476 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 477 t.Fatalf("unable to make dstDir: %s", err) 478 } 479 480 dstDir = joinTrailingSep(tmpDirB, "dir1") 481 482 if err = testCopyHelper(t, srcPath, dstDir); err != nil { 483 t.Fatalf("unexpected error %T: %s", err, err) 484 } 485 486 err = fileContentsEqual(t, srcPath, dstPath) 487 assert.NilError(t, err) 488 } 489 490 // D. Symbol link following version: 491 // SRC specifies a file and DST exists as a directory. This should place 492 // a copy of the source file inside it using the basename from SRC. Ensure 493 // this works whether DST has a trailing path separator or not. 494 func TestCopyCaseDFSym(t *testing.T) { 495 tmpDirA, tmpDirB := getTestTempDirs(t) 496 defer removeAllPaths(tmpDirA, tmpDirB) 497 498 // Load A and B with some sample files and directories. 499 createSampleDir(t, tmpDirA) 500 createSampleDir(t, tmpDirB) 501 502 srcPath := filepath.Join(tmpDirA, "symlink4") 503 linkTarget := filepath.Join(tmpDirA, "file1") 504 dstDir := filepath.Join(tmpDirB, "dir1") 505 dstPath := filepath.Join(dstDir, "symlink4") 506 507 var err error 508 509 // Ensure that dstPath doesn't exist. 510 if _, err = os.Stat(dstPath); !os.IsNotExist(err) { 511 t.Fatalf("did not expect dstPath %q to exist", dstPath) 512 } 513 514 if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil { 515 t.Fatalf("unexpected error %T: %s", err, err) 516 } 517 518 err = fileContentsEqual(t, linkTarget, dstPath) 519 assert.NilError(t, err) 520 521 // Now try again but using a trailing path separator for dstDir. 522 523 if err = os.RemoveAll(dstDir); err != nil { 524 t.Fatalf("unable to remove dstDir: %s", err) 525 } 526 527 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 528 t.Fatalf("unable to make dstDir: %s", err) 529 } 530 531 dstDir = joinTrailingSep(tmpDirB, "dir1") 532 533 if err = testCopyHelperFSym(t, srcPath, dstDir); err != nil { 534 t.Fatalf("unexpected error %T: %s", err, err) 535 } 536 537 err = fileContentsEqual(t, linkTarget, dstPath) 538 assert.NilError(t, err) 539 } 540 541 // E. SRC specifies a directory and DST does not exist. This should create a 542 // directory at DST and copy the contents of the SRC directory into the DST 543 // directory. Ensure this works whether DST has a trailing path separator or 544 // not. 545 func TestCopyCaseE(t *testing.T) { 546 tmpDirA, tmpDirB := getTestTempDirs(t) 547 defer removeAllPaths(tmpDirA, tmpDirB) 548 549 // Load A with some sample files and directories. 550 createSampleDir(t, tmpDirA) 551 552 srcDir := filepath.Join(tmpDirA, "dir1") 553 dstDir := filepath.Join(tmpDirB, "testDir") 554 555 var err error 556 557 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 558 t.Fatalf("unexpected error %T: %s", err, err) 559 } 560 561 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 562 t.Log("dir contents not equal") 563 logDirContents(t, tmpDirA) 564 logDirContents(t, tmpDirB) 565 t.Fatal(err) 566 } 567 568 // Now try again but using a trailing path separator for dstDir. 569 570 if err = os.RemoveAll(dstDir); err != nil { 571 t.Fatalf("unable to remove dstDir: %s", err) 572 } 573 574 dstDir = joinTrailingSep(tmpDirB, "testDir") 575 576 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 577 t.Fatalf("unexpected error %T: %s", err, err) 578 } 579 580 err = dirContentsEqual(t, dstDir, srcDir) 581 assert.NilError(t, err) 582 } 583 584 // E. Symbol link following version: 585 // SRC specifies a directory and DST does not exist. This should create a 586 // directory at DST and copy the contents of the SRC directory into the DST 587 // directory. Ensure this works whether DST has a trailing path separator or 588 // not. 589 func TestCopyCaseEFSym(t *testing.T) { 590 tmpDirA, tmpDirB := getTestTempDirs(t) 591 defer removeAllPaths(tmpDirA, tmpDirB) 592 593 // Load A with some sample files and directories. 594 createSampleDir(t, tmpDirA) 595 596 srcDir := filepath.Join(tmpDirA, "dirSymlink") 597 linkTarget := filepath.Join(tmpDirA, "dir1") 598 dstDir := filepath.Join(tmpDirB, "testDir") 599 600 var err error 601 602 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 603 t.Fatalf("unexpected error %T: %s", err, err) 604 } 605 606 if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { 607 t.Log("dir contents not equal") 608 logDirContents(t, tmpDirA) 609 logDirContents(t, tmpDirB) 610 t.Fatal(err) 611 } 612 613 // Now try again but using a trailing path separator for dstDir. 614 615 if err = os.RemoveAll(dstDir); err != nil { 616 t.Fatalf("unable to remove dstDir: %s", err) 617 } 618 619 dstDir = joinTrailingSep(tmpDirB, "testDir") 620 621 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 622 t.Fatalf("unexpected error %T: %s", err, err) 623 } 624 625 err = dirContentsEqual(t, dstDir, linkTarget) 626 assert.NilError(t, err) 627 } 628 629 // F. SRC specifies a directory and DST exists as a file. This should cause an 630 // error as it is not possible to overwrite a file with a directory. 631 func TestCopyCaseF(t *testing.T) { 632 tmpDirA, tmpDirB := getTestTempDirs(t) 633 defer removeAllPaths(tmpDirA, tmpDirB) 634 635 // Load A and B with some sample files and directories. 636 createSampleDir(t, tmpDirA) 637 createSampleDir(t, tmpDirB) 638 639 srcDir := filepath.Join(tmpDirA, "dir1") 640 symSrcDir := filepath.Join(tmpDirA, "dirSymlink") 641 dstFile := filepath.Join(tmpDirB, "file1") 642 643 var err error 644 645 if err = testCopyHelper(t, srcDir, dstFile); err == nil { 646 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 647 } 648 649 if err != ErrCannotCopyDir { 650 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 651 } 652 653 // now test with symbol link 654 if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil { 655 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 656 } 657 658 if err != ErrCannotCopyDir { 659 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 660 } 661 } 662 663 // G. SRC specifies a directory and DST exists as a directory. This should copy 664 // the SRC directory and all its contents to the DST directory. Ensure this 665 // works whether DST has a trailing path separator or not. 666 func TestCopyCaseG(t *testing.T) { 667 tmpDirA, tmpDirB := getTestTempDirs(t) 668 defer removeAllPaths(tmpDirA, tmpDirB) 669 670 // Load A and B with some sample files and directories. 671 createSampleDir(t, tmpDirA) 672 createSampleDir(t, tmpDirB) 673 674 srcDir := filepath.Join(tmpDirA, "dir1") 675 dstDir := filepath.Join(tmpDirB, "dir2") 676 resultDir := filepath.Join(dstDir, "dir1") 677 678 var err error 679 680 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 681 t.Fatalf("unexpected error %T: %s", err, err) 682 } 683 684 err = dirContentsEqual(t, resultDir, srcDir) 685 assert.NilError(t, err) 686 687 // Now try again but using a trailing path separator for dstDir. 688 689 if err = os.RemoveAll(dstDir); err != nil { 690 t.Fatalf("unable to remove dstDir: %s", err) 691 } 692 693 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 694 t.Fatalf("unable to make dstDir: %s", err) 695 } 696 697 dstDir = joinTrailingSep(tmpDirB, "dir2") 698 699 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 700 t.Fatalf("unexpected error %T: %s", err, err) 701 } 702 703 err = dirContentsEqual(t, resultDir, srcDir) 704 assert.NilError(t, err) 705 } 706 707 // G. Symbol link version: 708 // SRC specifies a directory and DST exists as a directory. This should copy 709 // the SRC directory and all its contents to the DST directory. Ensure this 710 // works whether DST has a trailing path separator or not. 711 func TestCopyCaseGFSym(t *testing.T) { 712 tmpDirA, tmpDirB := getTestTempDirs(t) 713 defer removeAllPaths(tmpDirA, tmpDirB) 714 715 // Load A and B with some sample files and directories. 716 createSampleDir(t, tmpDirA) 717 createSampleDir(t, tmpDirB) 718 719 srcDir := filepath.Join(tmpDirA, "dirSymlink") 720 linkTarget := filepath.Join(tmpDirA, "dir1") 721 dstDir := filepath.Join(tmpDirB, "dir2") 722 resultDir := filepath.Join(dstDir, "dirSymlink") 723 724 var err error 725 726 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 727 t.Fatalf("unexpected error %T: %s", err, err) 728 } 729 730 err = dirContentsEqual(t, resultDir, linkTarget) 731 assert.NilError(t, err) 732 733 // Now try again but using a trailing path separator for dstDir. 734 735 if err = os.RemoveAll(dstDir); err != nil { 736 t.Fatalf("unable to remove dstDir: %s", err) 737 } 738 739 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 740 t.Fatalf("unable to make dstDir: %s", err) 741 } 742 743 dstDir = joinTrailingSep(tmpDirB, "dir2") 744 745 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 746 t.Fatalf("unexpected error %T: %s", err, err) 747 } 748 749 err = dirContentsEqual(t, resultDir, linkTarget) 750 assert.NilError(t, err) 751 } 752 753 // H. SRC specifies a directory's contents only and DST does not exist. This 754 // should create a directory at DST and copy the contents of the SRC 755 // directory (but not the directory itself) into the DST directory. Ensure 756 // this works whether DST has a trailing path separator or not. 757 func TestCopyCaseH(t *testing.T) { 758 tmpDirA, tmpDirB := getTestTempDirs(t) 759 defer removeAllPaths(tmpDirA, tmpDirB) 760 761 // Load A with some sample files and directories. 762 createSampleDir(t, tmpDirA) 763 764 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 765 dstDir := filepath.Join(tmpDirB, "testDir") 766 767 var err error 768 769 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 770 t.Fatalf("unexpected error %T: %s", err, err) 771 } 772 773 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 774 t.Log("dir contents not equal") 775 logDirContents(t, tmpDirA) 776 logDirContents(t, tmpDirB) 777 t.Fatal(err) 778 } 779 780 // Now try again but using a trailing path separator for dstDir. 781 782 if err = os.RemoveAll(dstDir); err != nil { 783 t.Fatalf("unable to remove dstDir: %s", err) 784 } 785 786 dstDir = joinTrailingSep(tmpDirB, "testDir") 787 788 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 789 t.Fatalf("unexpected error %T: %s", err, err) 790 } 791 792 if err = dirContentsEqual(t, dstDir, srcDir); err != nil { 793 t.Log("dir contents not equal") 794 logDirContents(t, tmpDirA) 795 logDirContents(t, tmpDirB) 796 t.Fatal(err) 797 } 798 } 799 800 // H. Symbol link following version: 801 // SRC specifies a directory's contents only and DST does not exist. This 802 // should create a directory at DST and copy the contents of the SRC 803 // directory (but not the directory itself) into the DST directory. Ensure 804 // this works whether DST has a trailing path separator or not. 805 func TestCopyCaseHFSym(t *testing.T) { 806 tmpDirA, tmpDirB := getTestTempDirs(t) 807 defer removeAllPaths(tmpDirA, tmpDirB) 808 809 // Load A with some sample files and directories. 810 createSampleDir(t, tmpDirA) 811 812 srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "." 813 linkTarget := filepath.Join(tmpDirA, "dir1") 814 dstDir := filepath.Join(tmpDirB, "testDir") 815 816 var err error 817 818 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 819 t.Fatalf("unexpected error %T: %s", err, err) 820 } 821 822 if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { 823 t.Log("dir contents not equal") 824 logDirContents(t, tmpDirA) 825 logDirContents(t, tmpDirB) 826 t.Fatal(err) 827 } 828 829 // Now try again but using a trailing path separator for dstDir. 830 831 if err = os.RemoveAll(dstDir); err != nil { 832 t.Fatalf("unable to remove dstDir: %s", err) 833 } 834 835 dstDir = joinTrailingSep(tmpDirB, "testDir") 836 837 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 838 t.Fatalf("unexpected error %T: %s", err, err) 839 } 840 841 if err = dirContentsEqual(t, dstDir, linkTarget); err != nil { 842 t.Log("dir contents not equal") 843 logDirContents(t, tmpDirA) 844 logDirContents(t, tmpDirB) 845 t.Fatal(err) 846 } 847 } 848 849 // I. SRC specifies a directory's contents only and DST exists as a file. This 850 // should cause an error as it is not possible to overwrite a file with a 851 // directory. 852 func TestCopyCaseI(t *testing.T) { 853 tmpDirA, tmpDirB := getTestTempDirs(t) 854 defer removeAllPaths(tmpDirA, tmpDirB) 855 856 // Load A and B with some sample files and directories. 857 createSampleDir(t, tmpDirA) 858 createSampleDir(t, tmpDirB) 859 860 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 861 symSrcDir := filepath.Join(tmpDirB, "dirSymlink") 862 dstFile := filepath.Join(tmpDirB, "file1") 863 864 var err error 865 866 if err = testCopyHelper(t, srcDir, dstFile); err == nil { 867 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 868 } 869 870 if err != ErrCannotCopyDir { 871 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 872 } 873 874 // now try with symbol link of dir 875 if err = testCopyHelperFSym(t, symSrcDir, dstFile); err == nil { 876 t.Fatal("expected ErrCannotCopyDir error, but got nil instead") 877 } 878 879 if err != ErrCannotCopyDir { 880 t.Fatalf("expected ErrCannotCopyDir error, but got %T: %s", err, err) 881 } 882 } 883 884 // J. SRC specifies a directory's contents only and DST exists as a directory. 885 // This should copy the contents of the SRC directory (but not the directory 886 // itself) into the DST directory. Ensure this works whether DST has a 887 // trailing path separator or not. 888 func TestCopyCaseJ(t *testing.T) { 889 tmpDirA, tmpDirB := getTestTempDirs(t) 890 defer removeAllPaths(tmpDirA, tmpDirB) 891 892 // Load A and B with some sample files and directories. 893 createSampleDir(t, tmpDirA) 894 createSampleDir(t, tmpDirB) 895 896 srcDir := joinTrailingSep(tmpDirA, "dir1") + "." 897 dstDir := filepath.Join(tmpDirB, "dir5") 898 899 var err error 900 901 // first to create an empty dir 902 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 903 t.Fatalf("unable to make dstDir: %s", err) 904 } 905 906 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 907 t.Fatalf("unexpected error %T: %s", err, err) 908 } 909 910 err = dirContentsEqual(t, dstDir, srcDir) 911 assert.NilError(t, err) 912 913 // Now try again but using a trailing path separator for dstDir. 914 915 if err = os.RemoveAll(dstDir); err != nil { 916 t.Fatalf("unable to remove dstDir: %s", err) 917 } 918 919 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 920 t.Fatalf("unable to make dstDir: %s", err) 921 } 922 923 dstDir = joinTrailingSep(tmpDirB, "dir5") 924 925 if err = testCopyHelper(t, srcDir, dstDir); err != nil { 926 t.Fatalf("unexpected error %T: %s", err, err) 927 } 928 929 err = dirContentsEqual(t, dstDir, srcDir) 930 assert.NilError(t, err) 931 } 932 933 // J. Symbol link following version: 934 // SRC specifies a directory's contents only and DST exists as a directory. 935 // This should copy the contents of the SRC directory (but not the directory 936 // itself) into the DST directory. Ensure this works whether DST has a 937 // trailing path separator or not. 938 func TestCopyCaseJFSym(t *testing.T) { 939 tmpDirA, tmpDirB := getTestTempDirs(t) 940 defer removeAllPaths(tmpDirA, tmpDirB) 941 942 // Load A and B with some sample files and directories. 943 createSampleDir(t, tmpDirA) 944 createSampleDir(t, tmpDirB) 945 946 srcDir := joinTrailingSep(tmpDirA, "dirSymlink") + "." 947 linkTarget := filepath.Join(tmpDirA, "dir1") 948 dstDir := filepath.Join(tmpDirB, "dir5") 949 950 var err error 951 952 // first to create an empty dir 953 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 954 t.Fatalf("unable to make dstDir: %s", err) 955 } 956 957 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 958 t.Fatalf("unexpected error %T: %s", err, err) 959 } 960 961 err = dirContentsEqual(t, dstDir, linkTarget) 962 assert.NilError(t, err) 963 964 // Now try again but using a trailing path separator for dstDir. 965 966 if err = os.RemoveAll(dstDir); err != nil { 967 t.Fatalf("unable to remove dstDir: %s", err) 968 } 969 970 if err = os.MkdirAll(dstDir, os.FileMode(0755)); err != nil { 971 t.Fatalf("unable to make dstDir: %s", err) 972 } 973 974 dstDir = joinTrailingSep(tmpDirB, "dir5") 975 976 if err = testCopyHelperFSym(t, srcDir, dstDir); err != nil { 977 t.Fatalf("unexpected error %T: %s", err, err) 978 } 979 980 err = dirContentsEqual(t, dstDir, linkTarget) 981 assert.NilError(t, err) 982 }