github.com/saymoon/flop@v0.1.6-0.20201205092451-00912199cc96/copy_test.go (about) 1 package flop 2 3 import ( 4 "fmt" 5 "github.com/stretchr/testify/assert" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "testing" 10 ) 11 12 // debug will perform advanced logging if set to true 13 // set to false to keep test results more terse 14 const debug = true 15 16 func TestFileContentInCopy(t *testing.T) { 17 assert := assert.New(t) 18 tests := []struct { 19 name string 20 inFile string 21 outFile string 22 }{ 23 //{ 24 // name: "basic_file_copy", 25 // inFile: tmpFile(), 26 // outFile: tmpFile(), 27 //}, 28 { 29 name: "unused_dst_file_path", 30 inFile: tmpFile(), 31 outFile: tmpFilePathUnused(), 32 }, 33 } 34 for _, tt := range tests { 35 t.Run(tt.name, func(t *testing.T) { 36 content := []byte("foo") 37 38 err := ioutil.WriteFile(tt.inFile, content, 0655) 39 assert.Nil(err) 40 41 err = SimpleCopy(tt.inFile, tt.outFile) 42 assert.Nil(err, "err is:", err) 43 44 outFileContent, err := ioutil.ReadFile(tt.outFile) 45 assert.Nil(err) 46 assert.Equal(content, outFileContent) 47 }) 48 } 49 } 50 51 func TestErrorsInCopy(t *testing.T) { 52 assert := assert.New(t) 53 tests := []struct { 54 name string 55 inFile string 56 outFile string 57 options Options 58 errExpected bool 59 errSubstringExpected string 60 }{ 61 { 62 name: "file_does_not_exist", 63 inFile: tmpFilePathUnused(), 64 outFile: tmpFile(), 65 errExpected: true, 66 errSubstringExpected: ErrFileNotExist.Error(), 67 }, 68 { 69 name: "file_exist", 70 inFile: tmpFile(), 71 outFile: tmpFile(), 72 errExpected: false, 73 }, 74 { 75 name: "dst_path_which_cannot_be_opened_or_created", 76 inFile: tmpFile(), 77 outFile: "/path/that/is/inaccessible", 78 errExpected: true, 79 options: Options{Atomic: false}, 80 //errSubstringExpected: ErrCannotOpenOrCreateDstFile.Error(), 81 }, 82 { 83 name: "atomic_dst_path_which_cannot_be_opened_or_created", 84 inFile: tmpFile(), 85 outFile: "/path/that/is/inaccessible", 86 errExpected: true, 87 options: Options{Atomic: true}, 88 errSubstringExpected: ErrCannotCreateTmpFile.Error(), 89 }, 90 { 91 name: "src_directory_when_recurse_is_set_false", 92 inFile: tmpDirPath(), 93 outFile: tmpDirPath(), 94 options: Options{ 95 Recursive: false, 96 }, 97 errExpected: true, 98 errSubstringExpected: ErrOmittingDir.Error(), 99 }, 100 { 101 name: "verify_cannot_overwrite_file_with_dir", 102 inFile: tmpDirPath(), 103 outFile: tmpFile(), 104 options: Options{Recursive: true}, 105 errExpected: true, 106 errSubstringExpected: ErrCannotOverwriteNonDir.Error(), 107 }, 108 { 109 name: "err_when_parents_true_but_dst_is_not_dir", 110 inFile: tmpDirPath(), 111 outFile: tmpFile(), 112 options: Options{Parents: true}, 113 errExpected: true, 114 errSubstringExpected: ErrWithParentsDstMustBeDir.Error(), 115 }, 116 } 117 for _, tt := range tests { 118 t.Run(tt.name, func(t *testing.T) { 119 err := Copy(tt.inFile, tt.outFile, tt.options) 120 if tt.errExpected { 121 assert.True(errContains(err, tt.errSubstringExpected), fmt.Sprintf("err '%s' does not contain '%s'", err, tt.errSubstringExpected)) 122 } else { 123 assert.Nil(err) 124 } 125 }) 126 } 127 } 128 129 func TestCopyingBasicFileContentsWhenDstFileExists(t *testing.T) { 130 assert := assert.New(t) 131 src, dst := tmpFile(), tmpFile() 132 content := []byte("foo") 133 134 err := ioutil.WriteFile(src, content, 0655) 135 assert.Nil(err) 136 137 err = SimpleCopy(src, dst) 138 assert.Nil(err) 139 140 b, err := ioutil.ReadFile(dst) 141 assert.Nil(err) 142 assert.Equal(content, b) 143 } 144 145 func TestCopyingBasicFileContentsWhenDstFileDoesNotExist(t *testing.T) { 146 assert := assert.New(t) 147 src := tmpFile() 148 dst := tmpFilePathUnused() 149 content := []byte("foo") 150 err := ioutil.WriteFile(src, content, 0655) 151 assert.Nil(err) 152 153 err = SimpleCopy(src, dst) 154 assert.Nil(err) 155 156 b, err := ioutil.ReadFile(dst) 157 assert.Nil(err) 158 assert.Equal(content, b) 159 } 160 161 func TestCopyingSameFileReturnsNoError(t *testing.T) { 162 assert := assert.New(t) 163 tmp := tmpFile() 164 err := SimpleCopy(tmp, tmp) 165 assert.Nil(err) 166 } 167 168 func TestCopySrcFileToDstDir(t *testing.T) { 169 assert := assert.New(t) 170 src := tmpFile() 171 content := []byte("foo") 172 173 tests := []struct { 174 name string 175 dst string 176 options Options 177 errExpected bool 178 errSubstringExpected string 179 }{ 180 { 181 name: "auto_write_files_to_dirs", 182 dst: tmpDirPath(), 183 options: Options{AppendNameToPath: true}, 184 }, 185 { 186 name: "auto_write_files_to_dirs", 187 dst: tmpDirPath(), 188 options: Options{AppendNameToPath: false}, 189 errExpected: true, 190 errSubstringExpected: ErrWritingFileToExistingDir.Error(), 191 }, 192 } 193 194 for _, tt := range tests { 195 t.Run(tt.name, func(t *testing.T) { 196 err := ioutil.WriteFile(src, content, 0655) 197 assert.Nil(err) 198 199 // make sure we don't error 200 err = Copy(src, tt.dst, tt.options) 201 if tt.errExpected { 202 assert.True(errContains(err, tt.errSubstringExpected)) 203 } else { 204 assert.Nil(err) 205 // make sure the dst inFile is in the dir where we expect it 206 b, err := ioutil.ReadFile(filepath.Join(tt.dst, filepath.Base(src))) 207 assert.Nil(err) 208 assert.Equal(content, b) 209 } 210 }) 211 } 212 } 213 214 func TestCopyingDirToDirWhenDstDirDoesNotExist(t *testing.T) { 215 assert := assert.New(t) 216 src, dst := tmpDirPath(), tmpDirPathUnused() 217 218 // make files with content 219 file1 := filepath.Join(src, "file1.txt") 220 assert.Nil(os.Mkdir(filepath.Join(src, "subdir"), 0777)) 221 file3 := filepath.Join(src, "subdir", "file2.txt") 222 files := []string{file1, file3} 223 224 // write content to files 225 content := []byte("foo") 226 for _, file := range files { 227 assert.Nil(ioutil.WriteFile(file, content, 0655)) 228 } 229 230 // ensure src exists 231 _, err := os.Open(src) 232 assert.False(os.IsNotExist(err)) 233 234 // copy directory to directory recursively 235 assert.Nil(Copy(src, dst, Options{Recursive: true})) 236 237 // verify the file contents 238 assert.Nil(err) 239 for _, file := range files { 240 b, err := ioutil.ReadFile(file) 241 assert.Nil(err) 242 assert.Equal(content, b) 243 } 244 } 245 246 func TestCopyingDirToDirWhenDstContainsTrailingSlash(t *testing.T) { 247 // at some point we try to determine if the dst directory's parent 248 // exists. when given /some/path the parent is /some. when given 249 // /some/path/ the parent is /some/path. this means the destination 250 // dir is not created when it should be. this tests for that case. 251 252 assert := assert.New(t) 253 src, dst := tmpDirPath(), tmpDirPathUnused()+"/" 254 255 // make files with content 256 file1 := filepath.Join(src, "file1.txt") 257 assert.Nil(os.Mkdir(filepath.Join(src, "subdir"), 0777)) 258 file3 := filepath.Join(src, "subdir", "file2.txt") 259 files := []string{file1, file3} 260 261 // write content to files 262 content := []byte("foo") 263 for _, file := range files { 264 assert.Nil(ioutil.WriteFile(file, content, 0655)) 265 } 266 267 // ensure src exists 268 _, err := os.Open(src) 269 assert.False(os.IsNotExist(err)) 270 271 // copy directory to directory recursively 272 assert.Nil(Copy(src, dst, Options{Recursive: true})) 273 274 // verify the file contents 275 assert.Nil(err) 276 for _, file := range files { 277 b, err := ioutil.ReadFile(file) 278 assert.Nil(err) 279 assert.Equal(content, b) 280 } 281 } 282 283 func TestCopyingFileWithParentsFlagCreatesParentDirs(t *testing.T) { 284 assert := assert.New(t) 285 workDir := tmpDirPath() 286 err := os.Chdir(workDir) 287 assert.Nil(err) 288 289 // create nested path 290 nestedPath := "nested/path/" 291 assert.Nil(os.MkdirAll(nestedPath, 0777)) 292 293 // make destination dir 294 dst := "dest" 295 assert.Nil(os.Mkdir(dst, 0777)) 296 297 // make files with content 298 content := []byte("foo") 299 fileName := "nestedFile.txt" 300 nestedFile := filepath.Join(nestedPath, fileName) 301 302 // write content to file 303 assert.Nil(ioutil.WriteFile(nestedFile, content, 0655)) 304 305 // ensure nested exists just to be sure 306 _, err = os.Open(nestedFile) 307 assert.False(os.IsNotExist(err)) 308 309 assert.Nil(Copy(nestedFile, dst, Options{ 310 Recursive: true, 311 Parents: true, 312 DebugLogFunc: debugLogger, 313 InfoLogFunc: infoLogger, 314 })) 315 316 expectedFile := filepath.Join(dst, nestedPath, fileName) 317 318 // ensure the file exists where we expect it to 319 var exists bool 320 if _, err = os.Stat(expectedFile); !os.IsNotExist(err) { 321 exists = true 322 } 323 assert.True(exists) 324 } 325 326 func TestCopyingDirWithParentsFlagCreatesParentDirs(t *testing.T) { 327 assert := assert.New(t) 328 workDir := tmpDirPath() 329 assert.Nil(os.Chdir(workDir)) 330 331 nestedPath := "nested/path/" 332 assert.Nil(os.MkdirAll(nestedPath, 0777)) 333 334 // make destination dir 335 dst := "dest" 336 assert.Nil(os.Mkdir(dst, 0777)) 337 338 // make files with content 339 content := []byte("foo") 340 fileName := "nestedFile.txt" 341 nestedFile := filepath.Join(nestedPath, fileName) 342 343 // write content to file 344 assert.Nil(ioutil.WriteFile(nestedFile, content, 0655)) 345 346 // ensure nested exists just to be sure 347 _, err := os.Open(nestedFile) 348 assert.False(os.IsNotExist(err)) 349 350 assert.Nil(Copy(nestedPath, dst, Options{Recursive: true, Parents: true})) 351 352 expectedFile := filepath.Join(dst, nestedPath, fileName) 353 354 // ensure the file exists where we expect it to 355 var exists bool 356 if _, err = os.Stat(expectedFile); !os.IsNotExist(err) { 357 exists = true 358 } 359 assert.True(exists) 360 b, err := ioutil.ReadFile(expectedFile) 361 assert.Nil(err) 362 assert.Equal(content, b) 363 } 364 365 func TestNoClobberFile(t *testing.T) { 366 assert := assert.New(t) 367 tests := []struct { 368 name string 369 opts Options 370 // do we expect the dst file to be overwritten? 371 expectOverwrite bool 372 }{ 373 { 374 name: "expect_clobber", 375 opts: Options{NoClobber: false}, 376 expectOverwrite: true, 377 }, 378 { 379 name: "basic_no_clobber", 380 opts: Options{NoClobber: true}, 381 expectOverwrite: false, 382 }, 383 } 384 for _, tt := range tests { 385 t.Run(tt.name, func(t *testing.T) { 386 srcContent := []byte("source") 387 dstContent := []byte("dest") 388 389 src, dst := tmpFile(), tmpFile() 390 391 assert.Nil(ioutil.WriteFile(src, srcContent, 0655)) 392 assert.Nil(ioutil.WriteFile(dst, dstContent, 0655)) 393 394 assert.Nil(Copy(src, dst, tt.opts)) 395 b, err := ioutil.ReadFile(dst) 396 assert.Nil(err) 397 if tt.expectOverwrite { 398 assert.Equal(srcContent, b) 399 } else { 400 assert.Equal(dstContent, b) 401 } 402 }) 403 } 404 } 405 406 func TestNoErrWhenCopyfileAlreadyExists(t *testing.T) { 407 assert := assert.New(t) 408 src, dst := tmpFile(), tmpFile() 409 dstDir := filepath.Dir(dst) 410 411 // use the known expected name for a copy file 412 copyFileName := "copyfile-" 413 copyFileFullyQualified := filepath.Join(dstDir, copyFileName) 414 assert.Nil(ioutil.WriteFile(copyFileFullyQualified, []byte("foo"), 0655)) 415 // hold the copy file open to give our atomic copy code problems 416 f, err := os.Open(copyFileFullyQualified) 417 assert.Nil(err) 418 419 assert.Nil(Copy(src, dst, Options{Atomic: true})) 420 _ = f.Close() 421 } 422 423 func TestCreatingSimpleBackupFile(t *testing.T) { 424 assert := assert.New(t) 425 src, dst := tmpFile(), tmpFile() 426 427 content := []byte("foo") 428 assert.Nil(ioutil.WriteFile(dst, content, 0655)) 429 430 assert.Nil(Copy(src, dst, Options{Backup: "simple"})) 431 expectedBkpFile := dst + "~" 432 433 _, err := os.Stat(expectedBkpFile) 434 assert.Nil(err) 435 assert.False(os.IsNotExist(err)) 436 437 b, err := ioutil.ReadFile(expectedBkpFile) 438 assert.Nil(err) 439 assert.Equal(content, b) 440 } 441 442 func TestCreatingExistingBackupFilesWhenBackupIsNotPresent(t *testing.T) { 443 assert := assert.New(t) 444 src, dst := tmpFile(), tmpFile() 445 446 content := []byte("foo") 447 assert.Nil(ioutil.WriteFile(dst, content, 0655)) 448 449 assert.Nil(Copy(src, dst, Options{Backup: "existing"})) 450 451 expectedBkpFile := dst + "~" 452 _, err := os.Stat(expectedBkpFile) 453 assert.Nil(err) 454 assert.False(os.IsNotExist(err)) 455 456 b, err := ioutil.ReadFile(expectedBkpFile) 457 assert.Nil(err) 458 assert.Equal(content, b) 459 } 460 461 func TestNumberedBackupFileRegex(t *testing.T) { 462 assert := assert.New(t) 463 tests := []struct { 464 in string 465 expectMatch bool 466 msg string 467 }{ 468 {"f.txt.~1~", true, ""}, 469 {"f.txt~1~", false, "missing . before the ~"}, 470 {"f.txt.~12~", true, ""}, 471 {"f.txt.~12345~", true, ""}, 472 {"f.txt.~123456789~", false, "we don't expect to see numbers this long"}, 473 {"f.txt.~1x5~", false, "alpha in between numbers"}, 474 {"f.txt~", false, ""}, 475 {"f.t123xt", false, ""}, 476 {"123", false, ""}, 477 } 478 for _, tt := range tests { 479 t.Run(tt.in, func(t *testing.T) { 480 assert.Equal(tt.expectMatch, numberedBackupFile.MatchString(tt.in), tt.msg) 481 }) 482 } 483 } 484 485 func TestNumberedBackupRegexSubstring(t *testing.T) { 486 assert := assert.New(t) 487 tests := []struct { 488 in string 489 sub string // we expect to see this substring in index 1 490 }{ 491 {"f.txt.~1~", "1"}, 492 {"f.txt.~12~", "12"}, 493 {"123.456.~78~", "78"}, 494 } 495 for _, tt := range tests { 496 t.Run(tt.in, func(t *testing.T) { 497 sub := numberedBackupFile.FindStringSubmatch(tt.in) 498 if len(sub) == 0 { 499 assert.FailNow("substring has length 0") 500 } 501 if len(sub) <= 1 { 502 assert.FailNow("substring was not greater than 1 as we expect") 503 } 504 assert.Equal(string(sub[1]), tt.sub) 505 }) 506 } 507 } 508 509 //noinspection ALL 510 func TestCreatingNumberedBackupFilesWhenBackupFilesAreNumbered(t *testing.T) { 511 assert := assert.New(t) 512 tests := []struct { 513 name string 514 numberedFilesToCreate []int 515 expectedFileNum int 516 }{ 517 {"single_backup_file", []int{1}, 2}, 518 {"multiple_files", []int{1, 2}, 3}, 519 {"skip_files", []int{1, 3}, 4}, 520 {"skip_first_num", []int{2, 3}, 4}, 521 {"larger_numbers_just_for_good_measure", []int{5, 7, 5431}, 5432}, 522 } 523 for _, tt := range tests { 524 t.Run(tt.name, func(t *testing.T) { 525 src, dst := tmpFile(), tmpFile() 526 content := []byte("foo") 527 assert.Nil(ioutil.WriteFile(dst, content, 0655)) 528 defer os.Remove(dst) 529 530 // write all of the numbered files so we have a starting point 531 for _, num := range tt.numberedFilesToCreate { 532 numberedFile := fmt.Sprintf("%s.~%d~", dst, num) 533 assert.Nil(ioutil.WriteFile(numberedFile, content, 0655)) 534 defer os.Remove(numberedFile) 535 } 536 537 assert.Nil(Copy(src, dst, Options{Backup: "numbered"})) 538 expectedBkpFile := fmt.Sprintf("%s.~%d~", dst, tt.expectedFileNum) 539 b, err := ioutil.ReadFile(expectedBkpFile) 540 assert.Nil(err) 541 assert.Equal(content, b) 542 }) 543 } 544 }