github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/pkg/fileutils/fileutils_test.go (about) 1 package fileutils 2 3 import ( 4 "io/ioutil" 5 "os" 6 "path" 7 "path/filepath" 8 "runtime" 9 "strings" 10 "testing" 11 ) 12 13 // CopyFile with invalid src 14 func TestCopyFileWithInvalidSrc(t *testing.T) { 15 tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") 16 defer os.RemoveAll(tempFolder) 17 if err != nil { 18 t.Fatal(err) 19 } 20 bytes, err := CopyFile("/invalid/file/path", path.Join(tempFolder, "dest")) 21 if err == nil { 22 t.Fatal("Should have fail to copy an invalid src file") 23 } 24 if bytes != 0 { 25 t.Fatal("Should have written 0 bytes") 26 } 27 28 } 29 30 // CopyFile with invalid dest 31 func TestCopyFileWithInvalidDest(t *testing.T) { 32 tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") 33 defer os.RemoveAll(tempFolder) 34 if err != nil { 35 t.Fatal(err) 36 } 37 src := path.Join(tempFolder, "file") 38 err = ioutil.WriteFile(src, []byte("content"), 0740) 39 if err != nil { 40 t.Fatal(err) 41 } 42 bytes, err := CopyFile(src, path.Join(tempFolder, "/invalid/dest/path")) 43 if err == nil { 44 t.Fatal("Should have fail to copy an invalid src file") 45 } 46 if bytes != 0 { 47 t.Fatal("Should have written 0 bytes") 48 } 49 50 } 51 52 // CopyFile with same src and dest 53 func TestCopyFileWithSameSrcAndDest(t *testing.T) { 54 tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") 55 defer os.RemoveAll(tempFolder) 56 if err != nil { 57 t.Fatal(err) 58 } 59 file := path.Join(tempFolder, "file") 60 err = ioutil.WriteFile(file, []byte("content"), 0740) 61 if err != nil { 62 t.Fatal(err) 63 } 64 bytes, err := CopyFile(file, file) 65 if err != nil { 66 t.Fatal(err) 67 } 68 if bytes != 0 { 69 t.Fatal("Should have written 0 bytes as it is the same file.") 70 } 71 } 72 73 // CopyFile with same src and dest but path is different and not clean 74 func TestCopyFileWithSameSrcAndDestWithPathNameDifferent(t *testing.T) { 75 tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") 76 defer os.RemoveAll(tempFolder) 77 if err != nil { 78 t.Fatal(err) 79 } 80 testFolder := path.Join(tempFolder, "test") 81 err = os.MkdirAll(testFolder, 0740) 82 if err != nil { 83 t.Fatal(err) 84 } 85 file := path.Join(testFolder, "file") 86 sameFile := testFolder + "/../test/file" 87 err = ioutil.WriteFile(file, []byte("content"), 0740) 88 if err != nil { 89 t.Fatal(err) 90 } 91 bytes, err := CopyFile(file, sameFile) 92 if err != nil { 93 t.Fatal(err) 94 } 95 if bytes != 0 { 96 t.Fatal("Should have written 0 bytes as it is the same file.") 97 } 98 } 99 100 func TestCopyFile(t *testing.T) { 101 tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") 102 defer os.RemoveAll(tempFolder) 103 if err != nil { 104 t.Fatal(err) 105 } 106 src := path.Join(tempFolder, "src") 107 dest := path.Join(tempFolder, "dest") 108 ioutil.WriteFile(src, []byte("content"), 0777) 109 ioutil.WriteFile(dest, []byte("destContent"), 0777) 110 bytes, err := CopyFile(src, dest) 111 if err != nil { 112 t.Fatal(err) 113 } 114 if bytes != 7 { 115 t.Fatalf("Should have written %d bytes but wrote %d", 7, bytes) 116 } 117 actual, err := ioutil.ReadFile(dest) 118 if err != nil { 119 t.Fatal(err) 120 } 121 if string(actual) != "content" { 122 t.Fatalf("Dest content was '%s', expected '%s'", string(actual), "content") 123 } 124 } 125 126 // Reading a symlink to a directory must return the directory 127 func TestReadSymlinkedDirectoryExistingDirectory(t *testing.T) { 128 // TODO Windows: Port this test 129 if runtime.GOOS == "windows" { 130 t.Skip("Needs porting to Windows") 131 } 132 var err error 133 if err = os.Mkdir("/tmp/testReadSymlinkToExistingDirectory", 0777); err != nil { 134 t.Errorf("failed to create directory: %s", err) 135 } 136 137 if err = os.Symlink("/tmp/testReadSymlinkToExistingDirectory", "/tmp/dirLinkTest"); err != nil { 138 t.Errorf("failed to create symlink: %s", err) 139 } 140 141 var path string 142 if path, err = ReadSymlinkedDirectory("/tmp/dirLinkTest"); err != nil { 143 t.Fatalf("failed to read symlink to directory: %s", err) 144 } 145 146 if path != "/tmp/testReadSymlinkToExistingDirectory" { 147 t.Fatalf("symlink returned unexpected directory: %s", path) 148 } 149 150 if err = os.Remove("/tmp/testReadSymlinkToExistingDirectory"); err != nil { 151 t.Errorf("failed to remove temporary directory: %s", err) 152 } 153 154 if err = os.Remove("/tmp/dirLinkTest"); err != nil { 155 t.Errorf("failed to remove symlink: %s", err) 156 } 157 } 158 159 // Reading a non-existing symlink must fail 160 func TestReadSymlinkedDirectoryNonExistingSymlink(t *testing.T) { 161 var path string 162 var err error 163 if path, err = ReadSymlinkedDirectory("/tmp/test/foo/Non/ExistingPath"); err == nil { 164 t.Fatalf("error expected for non-existing symlink") 165 } 166 167 if path != "" { 168 t.Fatalf("expected empty path, but '%s' was returned", path) 169 } 170 } 171 172 // Reading a symlink to a file must fail 173 func TestReadSymlinkedDirectoryToFile(t *testing.T) { 174 // TODO Windows: Port this test 175 if runtime.GOOS == "windows" { 176 t.Skip("Needs porting to Windows") 177 } 178 var err error 179 var file *os.File 180 181 if file, err = os.Create("/tmp/testReadSymlinkToFile"); err != nil { 182 t.Fatalf("failed to create file: %s", err) 183 } 184 185 file.Close() 186 187 if err = os.Symlink("/tmp/testReadSymlinkToFile", "/tmp/fileLinkTest"); err != nil { 188 t.Errorf("failed to create symlink: %s", err) 189 } 190 191 var path string 192 if path, err = ReadSymlinkedDirectory("/tmp/fileLinkTest"); err == nil { 193 t.Fatalf("ReadSymlinkedDirectory on a symlink to a file should've failed") 194 } 195 196 if path != "" { 197 t.Fatalf("path should've been empty: %s", path) 198 } 199 200 if err = os.Remove("/tmp/testReadSymlinkToFile"); err != nil { 201 t.Errorf("failed to remove file: %s", err) 202 } 203 204 if err = os.Remove("/tmp/fileLinkTest"); err != nil { 205 t.Errorf("failed to remove symlink: %s", err) 206 } 207 } 208 209 func TestWildcardMatches(t *testing.T) { 210 match, _ := Matches("fileutils.go", []string{"*"}) 211 if match != true { 212 t.Errorf("failed to get a wildcard match, got %v", match) 213 } 214 } 215 216 // A simple pattern match should return true. 217 func TestPatternMatches(t *testing.T) { 218 match, _ := Matches("fileutils.go", []string{"*.go"}) 219 if match != true { 220 t.Errorf("failed to get a match, got %v", match) 221 } 222 } 223 224 // An exclusion followed by an inclusion should return true. 225 func TestExclusionPatternMatchesPatternBefore(t *testing.T) { 226 match, _ := Matches("fileutils.go", []string{"!fileutils.go", "*.go"}) 227 if match != true { 228 t.Errorf("failed to get true match on exclusion pattern, got %v", match) 229 } 230 } 231 232 // A folder pattern followed by an exception should return false. 233 func TestPatternMatchesFolderExclusions(t *testing.T) { 234 match, _ := Matches("docs/README.md", []string{"docs", "!docs/README.md"}) 235 if match != false { 236 t.Errorf("failed to get a false match on exclusion pattern, got %v", match) 237 } 238 } 239 240 // A folder pattern followed by an exception should return false. 241 func TestPatternMatchesFolderWithSlashExclusions(t *testing.T) { 242 match, _ := Matches("docs/README.md", []string{"docs/", "!docs/README.md"}) 243 if match != false { 244 t.Errorf("failed to get a false match on exclusion pattern, got %v", match) 245 } 246 } 247 248 // A folder pattern followed by an exception should return false. 249 func TestPatternMatchesFolderWildcardExclusions(t *testing.T) { 250 match, _ := Matches("docs/README.md", []string{"docs/*", "!docs/README.md"}) 251 if match != false { 252 t.Errorf("failed to get a false match on exclusion pattern, got %v", match) 253 } 254 } 255 256 // A pattern followed by an exclusion should return false. 257 func TestExclusionPatternMatchesPatternAfter(t *testing.T) { 258 match, _ := Matches("fileutils.go", []string{"*.go", "!fileutils.go"}) 259 if match != false { 260 t.Errorf("failed to get false match on exclusion pattern, got %v", match) 261 } 262 } 263 264 // A filename evaluating to . should return false. 265 func TestExclusionPatternMatchesWholeDirectory(t *testing.T) { 266 match, _ := Matches(".", []string{"*.go"}) 267 if match != false { 268 t.Errorf("failed to get false match on ., got %v", match) 269 } 270 } 271 272 // A single ! pattern should return an error. 273 func TestSingleExclamationError(t *testing.T) { 274 _, err := Matches("fileutils.go", []string{"!"}) 275 if err == nil { 276 t.Errorf("failed to get an error for a single exclamation point, got %v", err) 277 } 278 } 279 280 // A string preceded with a ! should return true from Exclusion. 281 func TestExclusion(t *testing.T) { 282 exclusion := exclusion("!") 283 if !exclusion { 284 t.Errorf("failed to get true for a single !, got %v", exclusion) 285 } 286 } 287 288 // Matches with no patterns 289 func TestMatchesWithNoPatterns(t *testing.T) { 290 matches, err := Matches("/any/path/there", []string{}) 291 if err != nil { 292 t.Fatal(err) 293 } 294 if matches { 295 t.Fatalf("Should not have match anything") 296 } 297 } 298 299 // Matches with malformed patterns 300 func TestMatchesWithMalformedPatterns(t *testing.T) { 301 matches, err := Matches("/any/path/there", []string{"["}) 302 if err == nil { 303 t.Fatal("Should have failed because of a malformed syntax in the pattern") 304 } 305 if matches { 306 t.Fatalf("Should not have match anything") 307 } 308 } 309 310 // Test lots of variants of patterns & strings 311 func TestMatches(t *testing.T) { 312 // TODO Windows: Port this test 313 if runtime.GOOS == "windows" { 314 t.Skip("Needs porting to Windows") 315 } 316 tests := []struct { 317 pattern string 318 text string 319 pass bool 320 }{ 321 {"**", "file", true}, 322 {"**", "file/", true}, 323 {"**/", "file", true}, // weird one 324 {"**/", "file/", true}, 325 {"**", "/", true}, 326 {"**/", "/", true}, 327 {"**", "dir/file", true}, 328 {"**/", "dir/file", false}, 329 {"**", "dir/file/", true}, 330 {"**/", "dir/file/", true}, 331 {"**/**", "dir/file", true}, 332 {"**/**", "dir/file/", true}, 333 {"dir/**", "dir/file", true}, 334 {"dir/**", "dir/file/", true}, 335 {"dir/**", "dir/dir2/file", true}, 336 {"dir/**", "dir/dir2/file/", true}, 337 {"**/dir2/*", "dir/dir2/file", true}, 338 {"**/dir2/*", "dir/dir2/file/", false}, 339 {"**/dir2/**", "dir/dir2/dir3/file", true}, 340 {"**/dir2/**", "dir/dir2/dir3/file/", true}, 341 {"**file", "file", true}, 342 {"**file", "dir/file", true}, 343 {"**/file", "dir/file", true}, 344 {"**file", "dir/dir/file", true}, 345 {"**/file", "dir/dir/file", true}, 346 {"**/file*", "dir/dir/file", true}, 347 {"**/file*", "dir/dir/file.txt", true}, 348 {"**/file*txt", "dir/dir/file.txt", true}, 349 {"**/file*.txt", "dir/dir/file.txt", true}, 350 {"**/file*.txt*", "dir/dir/file.txt", true}, 351 {"**/**/*.txt", "dir/dir/file.txt", true}, 352 {"**/**/*.txt2", "dir/dir/file.txt", false}, 353 {"**/*.txt", "file.txt", true}, 354 {"**/**/*.txt", "file.txt", true}, 355 {"a**/*.txt", "a/file.txt", true}, 356 {"a**/*.txt", "a/dir/file.txt", true}, 357 {"a**/*.txt", "a/dir/dir/file.txt", true}, 358 {"a/*.txt", "a/dir/file.txt", false}, 359 {"a/*.txt", "a/file.txt", true}, 360 {"a/*.txt**", "a/file.txt", true}, 361 {"a[b-d]e", "ae", false}, 362 {"a[b-d]e", "ace", true}, 363 {"a[b-d]e", "aae", false}, 364 {"a[^b-d]e", "aze", true}, 365 {".*", ".foo", true}, 366 {".*", "foo", false}, 367 {"abc.def", "abcdef", false}, 368 {"abc.def", "abc.def", true}, 369 {"abc.def", "abcZdef", false}, 370 {"abc?def", "abcZdef", true}, 371 {"abc?def", "abcdef", false}, 372 {"a\\*b", "a*b", true}, 373 {"a\\", "a", false}, 374 {"a\\", "a\\", false}, 375 {"a\\\\", "a\\", true}, 376 {"**/foo/bar", "foo/bar", true}, 377 {"**/foo/bar", "dir/foo/bar", true}, 378 {"**/foo/bar", "dir/dir2/foo/bar", true}, 379 {"abc/**", "abc", false}, 380 {"abc/**", "abc/def", true}, 381 {"abc/**", "abc/def/ghi", true}, 382 } 383 384 for _, test := range tests { 385 res, _ := regexpMatch(test.pattern, test.text) 386 if res != test.pass { 387 t.Fatalf("Failed: %v - res:%v", test, res) 388 } 389 } 390 } 391 392 // An empty string should return true from Empty. 393 func TestEmpty(t *testing.T) { 394 empty := empty("") 395 if !empty { 396 t.Errorf("failed to get true for an empty string, got %v", empty) 397 } 398 } 399 400 func TestCleanPatterns(t *testing.T) { 401 cleaned, _, _, _ := CleanPatterns([]string{"docs", "config"}) 402 if len(cleaned) != 2 { 403 t.Errorf("expected 2 element slice, got %v", len(cleaned)) 404 } 405 } 406 407 func TestCleanPatternsStripEmptyPatterns(t *testing.T) { 408 cleaned, _, _, _ := CleanPatterns([]string{"docs", "config", ""}) 409 if len(cleaned) != 2 { 410 t.Errorf("expected 2 element slice, got %v", len(cleaned)) 411 } 412 } 413 414 func TestCleanPatternsExceptionFlag(t *testing.T) { 415 _, _, exceptions, _ := CleanPatterns([]string{"docs", "!docs/README.md"}) 416 if !exceptions { 417 t.Errorf("expected exceptions to be true, got %v", exceptions) 418 } 419 } 420 421 func TestCleanPatternsLeadingSpaceTrimmed(t *testing.T) { 422 _, _, exceptions, _ := CleanPatterns([]string{"docs", " !docs/README.md"}) 423 if !exceptions { 424 t.Errorf("expected exceptions to be true, got %v", exceptions) 425 } 426 } 427 428 func TestCleanPatternsTrailingSpaceTrimmed(t *testing.T) { 429 _, _, exceptions, _ := CleanPatterns([]string{"docs", "!docs/README.md "}) 430 if !exceptions { 431 t.Errorf("expected exceptions to be true, got %v", exceptions) 432 } 433 } 434 435 func TestCleanPatternsErrorSingleException(t *testing.T) { 436 _, _, _, err := CleanPatterns([]string{"!"}) 437 if err == nil { 438 t.Errorf("expected error on single exclamation point, got %v", err) 439 } 440 } 441 442 func TestCleanPatternsFolderSplit(t *testing.T) { 443 _, dirs, _, _ := CleanPatterns([]string{"docs/config/CONFIG.md"}) 444 if dirs[0][0] != "docs" { 445 t.Errorf("expected first element in dirs slice to be docs, got %v", dirs[0][1]) 446 } 447 if dirs[0][1] != "config" { 448 t.Errorf("expected first element in dirs slice to be config, got %v", dirs[0][1]) 449 } 450 } 451 452 func TestCreateIfNotExistsDir(t *testing.T) { 453 tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") 454 if err != nil { 455 t.Fatal(err) 456 } 457 defer os.RemoveAll(tempFolder) 458 459 folderToCreate := filepath.Join(tempFolder, "tocreate") 460 461 if err := CreateIfNotExists(folderToCreate, true); err != nil { 462 t.Fatal(err) 463 } 464 fileinfo, err := os.Stat(folderToCreate) 465 if err != nil { 466 t.Fatalf("Should have create a folder, got %v", err) 467 } 468 469 if !fileinfo.IsDir() { 470 t.Fatalf("Should have been a dir, seems it's not") 471 } 472 } 473 474 func TestCreateIfNotExistsFile(t *testing.T) { 475 tempFolder, err := ioutil.TempDir("", "docker-fileutils-test") 476 if err != nil { 477 t.Fatal(err) 478 } 479 defer os.RemoveAll(tempFolder) 480 481 fileToCreate := filepath.Join(tempFolder, "file/to/create") 482 483 if err := CreateIfNotExists(fileToCreate, false); err != nil { 484 t.Fatal(err) 485 } 486 fileinfo, err := os.Stat(fileToCreate) 487 if err != nil { 488 t.Fatalf("Should have create a file, got %v", err) 489 } 490 491 if fileinfo.IsDir() { 492 t.Fatalf("Should have been a file, seems it's not") 493 } 494 } 495 496 // These matchTests are stolen from go's filepath Match tests. 497 type matchTest struct { 498 pattern, s string 499 match bool 500 err error 501 } 502 503 var matchTests = []matchTest{ 504 {"abc", "abc", true, nil}, 505 {"*", "abc", true, nil}, 506 {"*c", "abc", true, nil}, 507 {"a*", "a", true, nil}, 508 {"a*", "abc", true, nil}, 509 {"a*", "ab/c", false, nil}, 510 {"a*/b", "abc/b", true, nil}, 511 {"a*/b", "a/c/b", false, nil}, 512 {"a*b*c*d*e*/f", "axbxcxdxe/f", true, nil}, 513 {"a*b*c*d*e*/f", "axbxcxdxexxx/f", true, nil}, 514 {"a*b*c*d*e*/f", "axbxcxdxe/xxx/f", false, nil}, 515 {"a*b*c*d*e*/f", "axbxcxdxexxx/fff", false, nil}, 516 {"a*b?c*x", "abxbbxdbxebxczzx", true, nil}, 517 {"a*b?c*x", "abxbbxdbxebxczzy", false, nil}, 518 {"ab[c]", "abc", true, nil}, 519 {"ab[b-d]", "abc", true, nil}, 520 {"ab[e-g]", "abc", false, nil}, 521 {"ab[^c]", "abc", false, nil}, 522 {"ab[^b-d]", "abc", false, nil}, 523 {"ab[^e-g]", "abc", true, nil}, 524 {"a\\*b", "a*b", true, nil}, 525 {"a\\*b", "ab", false, nil}, 526 {"a?b", "a☺b", true, nil}, 527 {"a[^a]b", "a☺b", true, nil}, 528 {"a???b", "a☺b", false, nil}, 529 {"a[^a][^a][^a]b", "a☺b", false, nil}, 530 {"[a-ζ]*", "α", true, nil}, 531 {"*[a-ζ]", "A", false, nil}, 532 {"a?b", "a/b", false, nil}, 533 {"a*b", "a/b", false, nil}, 534 {"[\\]a]", "]", true, nil}, 535 {"[\\-]", "-", true, nil}, 536 {"[x\\-]", "x", true, nil}, 537 {"[x\\-]", "-", true, nil}, 538 {"[x\\-]", "z", false, nil}, 539 {"[\\-x]", "x", true, nil}, 540 {"[\\-x]", "-", true, nil}, 541 {"[\\-x]", "a", false, nil}, 542 {"[]a]", "]", false, filepath.ErrBadPattern}, 543 {"[-]", "-", false, filepath.ErrBadPattern}, 544 {"[x-]", "x", false, filepath.ErrBadPattern}, 545 {"[x-]", "-", false, filepath.ErrBadPattern}, 546 {"[x-]", "z", false, filepath.ErrBadPattern}, 547 {"[-x]", "x", false, filepath.ErrBadPattern}, 548 {"[-x]", "-", false, filepath.ErrBadPattern}, 549 {"[-x]", "a", false, filepath.ErrBadPattern}, 550 {"\\", "a", false, filepath.ErrBadPattern}, 551 {"[a-b-c]", "a", false, filepath.ErrBadPattern}, 552 {"[", "a", false, filepath.ErrBadPattern}, 553 {"[^", "a", false, filepath.ErrBadPattern}, 554 {"[^bc", "a", false, filepath.ErrBadPattern}, 555 {"a[", "a", false, filepath.ErrBadPattern}, // was nil but IMO its wrong 556 {"a[", "ab", false, filepath.ErrBadPattern}, 557 {"*x", "xxx", true, nil}, 558 } 559 560 func errp(e error) string { 561 if e == nil { 562 return "<nil>" 563 } 564 return e.Error() 565 } 566 567 // TestMatch test's our version of filepath.Match, called regexpMatch. 568 func TestMatch(t *testing.T) { 569 for _, tt := range matchTests { 570 pattern := tt.pattern 571 s := tt.s 572 if runtime.GOOS == "windows" { 573 if strings.Index(pattern, "\\") >= 0 { 574 // no escape allowed on windows. 575 continue 576 } 577 pattern = filepath.Clean(pattern) 578 s = filepath.Clean(s) 579 } 580 ok, err := regexpMatch(pattern, s) 581 if ok != tt.match || err != tt.err { 582 t.Fatalf("Match(%#q, %#q) = %v, %q want %v, %q", pattern, s, ok, errp(err), tt.match, errp(tt.err)) 583 } 584 } 585 }