tractor.dev/toolkit-go@v0.0.0-20241010005851-214d91207d07/engine/fs/watchfs/watcher/watcher_test.go (about) 1 package watcher 2 3 import ( 4 "io/fs" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "runtime" 9 "sync" 10 "testing" 11 "time" 12 ) 13 14 // setup creates all required files and folders for 15 // the tests and returns a function that is used as 16 // a teardown function when the tests are done. 17 func setup(t testing.TB) (string, func()) { 18 testDir, err := ioutil.TempDir(".", "") 19 if err != nil { 20 t.Fatal(err) 21 } 22 23 err = ioutil.WriteFile(filepath.Join(testDir, "file.txt"), 24 []byte{}, 0755) 25 if err != nil { 26 t.Fatal(err) 27 } 28 29 files := []string{"file_1.txt", "file_2.txt", "file_3.txt"} 30 31 for _, f := range files { 32 filePath := filepath.Join(testDir, f) 33 if err := ioutil.WriteFile(filePath, []byte{}, 0755); err != nil { 34 t.Fatal(err) 35 } 36 } 37 38 err = ioutil.WriteFile(filepath.Join(testDir, ".dotfile"), 39 []byte{}, 0755) 40 if err != nil { 41 t.Fatal(err) 42 } 43 44 testDirTwo := filepath.Join(testDir, "testDirTwo") 45 err = os.Mkdir(testDirTwo, 0755) 46 if err != nil { 47 t.Fatal(err) 48 } 49 50 err = ioutil.WriteFile(filepath.Join(testDirTwo, "file_recursive.txt"), 51 []byte{}, 0755) 52 if err != nil { 53 t.Fatal(err) 54 } 55 56 abs, err := filepath.Abs(testDir) 57 if err != nil { 58 os.RemoveAll(testDir) 59 t.Fatal(err) 60 } 61 if abs[0] == '/' { 62 abs = abs[1:] 63 } 64 return abs, func() { 65 if os.RemoveAll(testDir); err != nil { 66 t.Fatal(err) 67 } 68 } 69 } 70 71 func TestEventString(t *testing.T) { 72 e := &Event{Op: Create, Path: "/fake/path"} 73 74 testCases := []struct { 75 info fs.FileInfo 76 expected string 77 }{ 78 {nil, "???"}, 79 { 80 &fileInfo{name: "f1", dir: true}, 81 "DIRECTORY \"f1\" CREATE [/fake/path]", 82 }, 83 { 84 &fileInfo{name: "f2", dir: false}, 85 "FILE \"f2\" CREATE [/fake/path]", 86 }, 87 } 88 89 for _, tc := range testCases { 90 e.FileInfo = tc.info 91 if e.String() != tc.expected { 92 t.Errorf("expected e.String() to be %s, got %s", tc.expected, e.String()) 93 } 94 } 95 } 96 97 func TestFileInfo(t *testing.T) { 98 modTime := time.Now() 99 100 fInfo := &fileInfo{ 101 name: "finfo", 102 size: 1, 103 mode: fs.ModeDir, 104 modTime: modTime, 105 sys: nil, 106 dir: true, 107 } 108 109 // Test file info methods. 110 if fInfo.Name() != "finfo" { 111 t.Fatalf("expected fInfo.Name() to be 'finfo', got %s", fInfo.Name()) 112 } 113 if fInfo.IsDir() != true { 114 t.Fatalf("expected fInfo.IsDir() to be true, got %t", fInfo.IsDir()) 115 } 116 if fInfo.Size() != 1 { 117 t.Fatalf("expected fInfo.Size() to be 1, got %d", fInfo.Size()) 118 } 119 if fInfo.Sys() != nil { 120 t.Fatalf("expected fInfo.Sys() to be nil, got %v", fInfo.Sys()) 121 } 122 if fInfo.ModTime() != modTime { 123 t.Fatalf("expected fInfo.ModTime() to be %v, got %v", modTime, fInfo.ModTime()) 124 } 125 if fInfo.Mode() != fs.ModeDir { 126 t.Fatalf("expected fInfo.Mode() to be fs.ModeDir, got %#v", fInfo.Mode()) 127 } 128 129 w := New(os.DirFS("/")) 130 131 w.wg.Done() // Set the waitgroup to done. 132 133 go func() { 134 // Trigger an event with the file info. 135 w.TriggerEvent(Create, fInfo) 136 }() 137 138 e := <-w.Event 139 140 if e.FileInfo != fInfo { 141 t.Fatal("expected e.FileInfo to be equal to fInfo") 142 } 143 } 144 145 func TestRenameWatchedSubdir(t *testing.T) { 146 testDir, teardown := setup(t) 147 defer teardown() 148 149 w := New(os.DirFS("/")) 150 151 if err := w.Add(testDir); err != nil { 152 t.Fatal(err) 153 } 154 155 subdir := filepath.Join(testDir, "testDirTwo") 156 if err := w.Add(subdir); err != nil { 157 t.Fatal(err) 158 } 159 160 go func() { 161 // Start the watching process. 162 if err := w.Start(time.Millisecond); err != nil { 163 t.Fatal(err) 164 } 165 }() 166 167 os.Rename("/"+subdir, filepath.Join("/"+testDir, "testDirTwoNew")) 168 169 // go func() { 170 // for err := range w.Error { 171 // fmt.Println(err) 172 // } 173 // }() 174 175 <-w.Event // WRITE event to dir 176 rename := <-w.Event 177 if rename.Op != Rename { 178 t.Error("expected event for rename") 179 } 180 } 181 182 func TestWatcherAdd(t *testing.T) { 183 testDir, teardown := setup(t) 184 defer teardown() 185 186 w := New(os.DirFS("/")) 187 188 // Try to add a non-existing path. 189 err := w.Add("-") 190 if err == nil { 191 t.Error("expected error to not be nil") 192 } 193 194 if err := w.Add(testDir); err != nil { 195 t.Fatal(err) 196 } 197 198 if len(w.files) != 7 { 199 t.Errorf("expected len(w.files) to be 7, got %d", len(w.files)) 200 } 201 202 // Make sure w.names contains testDir 203 if _, found := w.names[testDir]; !found { 204 t.Errorf("expected w.names to contain testDir") 205 } 206 207 if _, found := w.files[testDir]; !found { 208 t.Errorf("expected to find %s", testDir) 209 } 210 211 if w.files[testDir].Name() != filepath.Base(testDir) { 212 t.Errorf("expected w.files[%q].Name() to be %s, got %s", 213 testDir, testDir, w.files[testDir].Name()) 214 } 215 216 dotFile := filepath.Join(testDir, ".dotfile") 217 if _, found := w.files[dotFile]; !found { 218 t.Errorf("expected to find %s", dotFile) 219 } 220 221 if w.files[dotFile].Name() != ".dotfile" { 222 t.Errorf("expected w.files[%q].Name() to be .dotfile, got %s", 223 dotFile, w.files[dotFile].Name()) 224 } 225 226 fileRecursive := filepath.Join(testDir, "testDirTwo", "file_recursive.txt") 227 if _, found := w.files[fileRecursive]; found { 228 t.Errorf("expected to not find %s", fileRecursive) 229 } 230 231 fileTxt := filepath.Join(testDir, "file.txt") 232 if _, found := w.files[fileTxt]; !found { 233 t.Errorf("expected to find %s", fileTxt) 234 } 235 236 if w.files[fileTxt].Name() != "file.txt" { 237 t.Errorf("expected w.files[%q].Name() to be file.txt, got %s", 238 fileTxt, w.files[fileTxt].Name()) 239 } 240 241 dirTwo := filepath.Join(testDir, "testDirTwo") 242 if _, found := w.files[dirTwo]; !found { 243 t.Errorf("expected to find %s directory", dirTwo) 244 } 245 246 if w.files[dirTwo].Name() != "testDirTwo" { 247 t.Errorf("expected w.files[%q].Name() to be testDirTwo, got %s", 248 dirTwo, w.files[dirTwo].Name()) 249 } 250 } 251 252 func TestIgnore(t *testing.T) { 253 testDir, teardown := setup(t) 254 defer teardown() 255 256 w := New(os.DirFS("/")) 257 258 err := w.Add(testDir) 259 if err != nil { 260 t.Errorf("expected error to be nil, got %s", err) 261 } 262 if len(w.files) != 7 { 263 t.Errorf("expected len(w.files) to be 7, got %d", len(w.files)) 264 } 265 266 err = w.Ignore(testDir) 267 if err != nil { 268 t.Errorf("expected error to be nil, got %s", err) 269 } 270 if len(w.files) != 0 { 271 t.Errorf("expected len(w.files) to be 0, got %d", len(w.files)) 272 } 273 274 // Now try to add the ignored directory. 275 err = w.Add(testDir) 276 if err != nil { 277 t.Errorf("expected error to be nil, got %s", err) 278 } 279 if len(w.files) != 0 { 280 t.Errorf("expected len(w.files) to be 0, got %d", len(w.files)) 281 } 282 } 283 284 func TestRemove(t *testing.T) { 285 testDir, teardown := setup(t) 286 defer teardown() 287 288 w := New(os.DirFS("/")) 289 290 err := w.Add(testDir) 291 if err != nil { 292 t.Errorf("expected error to be nil, got %s", err) 293 } 294 if len(w.files) != 7 { 295 t.Errorf("expected len(w.files) to be 7, got %d", len(w.files)) 296 } 297 298 err = w.Remove(testDir) 299 if err != nil { 300 t.Errorf("expected error to be nil, got %s", err) 301 } 302 if len(w.files) != 0 { 303 t.Errorf("expected len(w.files) to be 0, got %d", len(w.files)) 304 } 305 306 // TODO: Test remove single file. 307 } 308 309 // TODO: Test remove recursive function. 310 311 func TestIgnoreHiddenFilesRecursive(t *testing.T) { 312 // TODO: Write tests for ignore hidden on windows. 313 if runtime.GOOS == "windows" { 314 return 315 } 316 317 testDir, teardown := setup(t) 318 defer teardown() 319 320 w := New(os.DirFS("/")) 321 w.IgnoreHiddenFiles(true) 322 323 if err := w.AddRecursive(testDir); err != nil { 324 t.Fatal(err) 325 } 326 327 if len(w.files) != 7 { 328 t.Errorf("expected len(w.files) to be 7, got %d", len(w.files)) 329 } 330 331 // Make sure w.names contains testDir 332 if _, found := w.names[testDir]; !found { 333 t.Errorf("expected w.names to contain testDir") 334 } 335 336 if _, found := w.files[testDir]; !found { 337 t.Errorf("expected to find %s", testDir) 338 } 339 340 if w.files[testDir].Name() != filepath.Base(testDir) { 341 t.Errorf("expected w.files[%q].Name() to be %s, got %s", 342 testDir, filepath.Base(testDir), w.files[testDir].Name()) 343 } 344 345 fileRecursive := filepath.Join(testDir, "testDirTwo", "file_recursive.txt") 346 if _, found := w.files[fileRecursive]; !found { 347 t.Errorf("expected to find %s", fileRecursive) 348 } 349 350 if _, found := w.files[filepath.Join(testDir, ".dotfile")]; found { 351 t.Error("expected to not find .dotfile") 352 } 353 354 fileTxt := filepath.Join(testDir, "file.txt") 355 if _, found := w.files[fileTxt]; !found { 356 t.Errorf("expected to find %s", fileTxt) 357 } 358 359 if w.files[fileTxt].Name() != "file.txt" { 360 t.Errorf("expected w.files[%q].Name() to be file.txt, got %s", 361 fileTxt, w.files[fileTxt].Name()) 362 } 363 364 dirTwo := filepath.Join(testDir, "testDirTwo") 365 if _, found := w.files[dirTwo]; !found { 366 t.Errorf("expected to find %s directory", dirTwo) 367 } 368 369 if w.files[dirTwo].Name() != "testDirTwo" { 370 t.Errorf("expected w.files[%q].Name() to be testDirTwo, got %s", 371 dirTwo, w.files[dirTwo].Name()) 372 } 373 } 374 375 func TestIgnoreHiddenFiles(t *testing.T) { 376 // TODO: Write tests for ignore hidden on windows. 377 if runtime.GOOS == "windows" { 378 return 379 } 380 381 testDir, teardown := setup(t) 382 defer teardown() 383 384 w := New(os.DirFS("/")) 385 w.IgnoreHiddenFiles(true) 386 387 if err := w.Add(testDir); err != nil { 388 t.Fatal(err) 389 } 390 391 if len(w.files) != 6 { 392 t.Errorf("expected len(w.files) to be 6, got %d", len(w.files)) 393 } 394 395 // Make sure w.names contains testDir 396 if _, found := w.names[testDir]; !found { 397 t.Errorf("expected w.names to contain testDir") 398 } 399 400 if _, found := w.files[testDir]; !found { 401 t.Errorf("expected to find %s", testDir) 402 } 403 404 if w.files[testDir].Name() != filepath.Base(testDir) { 405 t.Errorf("expected w.files[%q].Name() to be %s, got %s", 406 testDir, filepath.Base(testDir), w.files[testDir].Name()) 407 } 408 409 if _, found := w.files[filepath.Join(testDir, ".dotfile")]; found { 410 t.Error("expected to not find .dotfile") 411 } 412 413 fileRecursive := filepath.Join(testDir, "testDirTwo", "file_recursive.txt") 414 if _, found := w.files[fileRecursive]; found { 415 t.Errorf("expected to not find %s", fileRecursive) 416 } 417 418 fileTxt := filepath.Join(testDir, "file.txt") 419 if _, found := w.files[fileTxt]; !found { 420 t.Errorf("expected to find %s", fileTxt) 421 } 422 423 if w.files[fileTxt].Name() != "file.txt" { 424 t.Errorf("expected w.files[%q].Name() to be file.txt, got %s", 425 fileTxt, w.files[fileTxt].Name()) 426 } 427 428 dirTwo := filepath.Join(testDir, "testDirTwo") 429 if _, found := w.files[dirTwo]; !found { 430 t.Errorf("expected to find %s directory", dirTwo) 431 } 432 433 if w.files[dirTwo].Name() != "testDirTwo" { 434 t.Errorf("expected w.files[%q].Name() to be testDirTwo, got %s", 435 dirTwo, w.files[dirTwo].Name()) 436 } 437 } 438 439 func TestWatcherAddRecursive(t *testing.T) { 440 testDir, teardown := setup(t) 441 defer teardown() 442 443 w := New(os.DirFS("/")) 444 445 if err := w.AddRecursive(testDir); err != nil { 446 t.Fatal(err) 447 } 448 449 // Make sure len(w.files) is 8. 450 if len(w.files) != 8 { 451 t.Errorf("expected 8 files, found %d", len(w.files)) 452 } 453 454 // Make sure w.names contains testDir 455 if _, found := w.names[testDir]; !found { 456 t.Errorf("expected w.names to contain testDir") 457 } 458 459 dirTwo := filepath.Join(testDir, "testDirTwo") 460 if _, found := w.files[dirTwo]; !found { 461 t.Errorf("expected to find %s directory", dirTwo) 462 } 463 464 if w.files[dirTwo].Name() != "testDirTwo" { 465 t.Errorf("expected w.files[%q].Name() to be testDirTwo, got %s", 466 "testDirTwo", w.files[dirTwo].Name()) 467 } 468 469 fileRecursive := filepath.Join(dirTwo, "file_recursive.txt") 470 if _, found := w.files[fileRecursive]; !found { 471 t.Errorf("expected to find %s directory", fileRecursive) 472 } 473 474 if w.files[fileRecursive].Name() != "file_recursive.txt" { 475 t.Errorf("expected w.files[%q].Name() to be file_recursive.txt, got %s", 476 fileRecursive, w.files[fileRecursive].Name()) 477 } 478 } 479 480 func TestWatcherAddNotFound(t *testing.T) { 481 w := New(os.DirFS("/")) 482 483 // Make sure there is an error when adding a 484 // non-existent file/folder. 485 if err := w.AddRecursive("random_filename.txt"); err == nil { 486 t.Error("expected a file not found error") 487 } 488 } 489 490 func TestWatcherRemoveRecursive(t *testing.T) { 491 testDir, teardown := setup(t) 492 defer teardown() 493 494 w := New(os.DirFS("/")) 495 496 // Add the testDir to the watchlist. 497 if err := w.AddRecursive(testDir); err != nil { 498 t.Fatal(err) 499 } 500 501 // Make sure len(w.files) is 8. 502 if len(w.files) != 8 { 503 t.Errorf("expected 8 files, found %d", len(w.files)) 504 } 505 506 // Now remove the folder from the watchlist. 507 if err := w.RemoveRecursive(testDir); err != nil { 508 t.Error(err) 509 } 510 511 // Now check that there is nothing being watched. 512 if len(w.files) != 0 { 513 t.Errorf("expected len(w.files) to be 0, got %d", len(w.files)) 514 } 515 516 // Make sure len(w.names) is now 0. 517 if len(w.names) != 0 { 518 t.Errorf("expected len(w.names) to be empty, len(w.names): %d", len(w.names)) 519 } 520 } 521 522 func TestListFiles(t *testing.T) { 523 testDir, teardown := setup(t) 524 defer teardown() 525 526 w := New(os.DirFS("/")) 527 w.AddRecursive(testDir) 528 529 fileList := w.retrieveFileList() 530 if fileList == nil { 531 t.Error("expected file list to not be empty") 532 } 533 534 // Make sure fInfoTest contains the correct fs.FileInfo names. 535 fname := filepath.Join(testDir, "file.txt") 536 if fileList[fname].Name() != "file.txt" { 537 t.Errorf("expected fileList[%s].Name() to be file.txt, got %s", 538 fname, fileList[fname].Name()) 539 } 540 541 // Try to call list on a file that's not a directory. 542 fileList, err := w.list(fname) 543 if err != nil { 544 t.Error("expected err to be nil") 545 } 546 if len(fileList) != 1 { 547 t.Errorf("expected len of file list to be 1, got %d", len(fileList)) 548 } 549 } 550 551 func TestTriggerEvent(t *testing.T) { 552 w := New(os.DirFS("/")) 553 554 var wg sync.WaitGroup 555 wg.Add(1) 556 557 go func() { 558 defer wg.Done() 559 560 select { 561 case event := <-w.Event: 562 if event.Name() != "triggered event" { 563 t.Errorf("expected event file name to be triggered event, got %s", 564 event.Name()) 565 } 566 case <-time.After(time.Millisecond * 250): 567 t.Fatal("received no event from Event channel") 568 } 569 }() 570 571 go func() { 572 // Start the watching process. 573 if err := w.Start(time.Millisecond * 100); err != nil { 574 t.Fatal(err) 575 } 576 }() 577 578 w.TriggerEvent(Create, nil) 579 580 wg.Wait() 581 } 582 583 func TestEventAddFile(t *testing.T) { 584 testDir, teardown := setup(t) 585 defer teardown() 586 587 w := New(os.DirFS("/")) 588 w.FilterOps(Create) 589 590 // Add the testDir to the watchlist. 591 if err := w.AddRecursive(testDir); err != nil { 592 t.Fatal(err) 593 } 594 595 files := map[string]bool{ 596 "newfile_1.txt": false, 597 "newfile_2.txt": false, 598 "newfile_3.txt": false, 599 } 600 601 for f := range files { 602 filePath := filepath.Join("/"+testDir, f) 603 if err := ioutil.WriteFile(filePath, []byte{}, 0755); err != nil { 604 t.Error(err) 605 } 606 } 607 608 var wg sync.WaitGroup 609 wg.Add(1) 610 611 go func() { 612 defer wg.Done() 613 614 events := 0 615 for { 616 select { 617 case event := <-w.Event: 618 if event.Op != Create { 619 t.Errorf("expected event to be Create, got %s", event.Op) 620 } 621 622 files[event.Name()] = true 623 events++ 624 625 // Check Path and OldPath content 626 newFile := filepath.Join(testDir, event.Name()) 627 if event.Path != newFile { 628 t.Errorf("Event.Path should be %s but got %s", newFile, event.Path) 629 } 630 if event.OldPath != "" { 631 t.Errorf("Event.OldPath should be empty on create, but got %s", event.OldPath) 632 } 633 634 if events == len(files) { 635 return 636 } 637 case <-time.After(time.Millisecond * 250): 638 for f, e := range files { 639 if !e { 640 t.Errorf("received no event for file %s", f) 641 } 642 } 643 return 644 } 645 } 646 }() 647 648 go func() { 649 // Start the watching process. 650 if err := w.Start(time.Millisecond * 100); err != nil { 651 t.Fatal(err) 652 } 653 }() 654 655 wg.Wait() 656 } 657 658 // TODO: TestIgnoreFiles 659 func TestIgnoreFiles(t *testing.T) {} 660 661 func TestEventDeleteFile(t *testing.T) { 662 testDir, teardown := setup(t) 663 defer teardown() 664 665 w := New(os.DirFS("/")) 666 w.FilterOps(Remove) 667 668 // Add the testDir to the watchlist. 669 if err := w.AddRecursive(testDir); err != nil { 670 t.Fatal(err) 671 } 672 673 files := map[string]bool{ 674 "file_1.txt": false, 675 "file_2.txt": false, 676 "file_3.txt": false, 677 } 678 679 for f := range files { 680 filePath := filepath.Join("/"+testDir, f) 681 if err := os.Remove(filePath); err != nil { 682 t.Error(err) 683 } 684 } 685 686 var wg sync.WaitGroup 687 wg.Add(1) 688 689 go func() { 690 defer wg.Done() 691 692 events := 0 693 for { 694 select { 695 case event := <-w.Event: 696 if event.Op != Remove { 697 t.Errorf("expected event to be Remove, got %s", event.Op) 698 } 699 700 files[event.Name()] = true 701 events++ 702 703 if events == len(files) { 704 return 705 } 706 case <-time.After(time.Millisecond * 250): 707 for f, e := range files { 708 if !e { 709 t.Errorf("received no event for file %s", f) 710 } 711 } 712 return 713 } 714 } 715 }() 716 717 go func() { 718 // Start the watching process. 719 if err := w.Start(time.Millisecond * 100); err != nil { 720 t.Fatal(err) 721 } 722 }() 723 724 wg.Wait() 725 } 726 727 func TestEventRenameFile(t *testing.T) { 728 testDir, teardown := setup(t) 729 defer teardown() 730 731 srcFilename := "file.txt" 732 dstFilename := "file1.txt" 733 734 w := New(os.DirFS("/")) 735 w.FilterOps(Rename) 736 737 // Add the testDir to the watchlist. 738 if err := w.AddRecursive(testDir); err != nil { 739 t.Fatal(err) 740 } 741 742 // Rename a file. 743 if err := os.Rename( 744 filepath.Join("/"+testDir, srcFilename), 745 filepath.Join("/"+testDir, dstFilename), 746 ); err != nil { 747 t.Error(err) 748 } 749 750 var wg sync.WaitGroup 751 wg.Add(1) 752 753 go func() { 754 defer wg.Done() 755 756 select { 757 case event := <-w.Event: 758 if event.Op != Rename { 759 t.Errorf("expected event to be Rename, got %s", event.Op) 760 } 761 762 // Check Path and OldPath content 763 oldFile := filepath.Join(testDir, srcFilename) 764 newFile := filepath.Join(testDir, dstFilename) 765 if event.Path != newFile { 766 t.Errorf("Event.Path should be %s but got %s", newFile, event.Path) 767 } 768 if event.OldPath != oldFile { 769 t.Errorf("Event.OldPath should %s but got %s", oldFile, event.OldPath) 770 } 771 772 case <-time.After(time.Millisecond * 250): 773 t.Fatal("received no rename event") 774 } 775 }() 776 777 go func() { 778 // Start the watching process. 779 if err := w.Start(time.Millisecond * 100); err != nil { 780 t.Fatal(err) 781 } 782 }() 783 784 wg.Wait() 785 } 786 787 func TestEventChmodFile(t *testing.T) { 788 // Chmod is not supported under windows. 789 if runtime.GOOS == "windows" { 790 return 791 } 792 793 testDir, teardown := setup(t) 794 defer teardown() 795 796 w := New(os.DirFS("/")) 797 w.FilterOps(Chmod) 798 799 // Add the testDir to the watchlist. 800 if err := w.Add(testDir); err != nil { 801 t.Fatal(err) 802 } 803 804 files := map[string]bool{ 805 "file_1.txt": false, 806 "file_2.txt": false, 807 "file_3.txt": false, 808 } 809 810 for f := range files { 811 filePath := filepath.Join("/"+testDir, f) 812 if err := os.Chmod(filePath, os.ModePerm); err != nil { 813 t.Error(err) 814 } 815 } 816 817 var wg sync.WaitGroup 818 wg.Add(1) 819 820 go func() { 821 defer wg.Done() 822 823 events := 0 824 for { 825 select { 826 case event := <-w.Event: 827 if event.Op != Chmod { 828 t.Errorf("expected event to be Remove, got %s", event.Op) 829 } 830 831 files[event.Name()] = true 832 events++ 833 834 if events == len(files) { 835 return 836 } 837 case <-time.After(time.Millisecond * 250): 838 for f, e := range files { 839 if !e { 840 t.Errorf("received no event for file %s", f) 841 } 842 } 843 return 844 } 845 } 846 }() 847 848 go func() { 849 // Start the watching process. 850 if err := w.Start(time.Millisecond * 100); err != nil { 851 t.Fatal(err) 852 } 853 }() 854 855 wg.Wait() 856 } 857 858 func TestWatcherStartWithInvalidDuration(t *testing.T) { 859 w := New(os.DirFS("/")) 860 861 err := w.Start(0) 862 if err != ErrDurationTooShort { 863 t.Fatalf("expected ErrDurationTooShort error, got %s", err.Error()) 864 } 865 } 866 867 func TestWatcherStartWhenAlreadyRunning(t *testing.T) { 868 w := New(os.DirFS("/")) 869 870 go func() { 871 err := w.Start(time.Millisecond * 100) 872 if err != nil { 873 t.Fatal(err) 874 } 875 }() 876 w.Wait() 877 878 err := w.Start(time.Millisecond * 100) 879 if err != ErrWatcherRunning { 880 t.Fatalf("expected ErrWatcherRunning error, got %s", err.Error()) 881 } 882 } 883 884 func BenchmarkEventRenameFile(b *testing.B) { 885 testDir, teardown := setup(b) 886 defer teardown() 887 888 w := New(os.DirFS("/")) 889 w.FilterOps(Rename) 890 891 // Add the testDir to the watchlist. 892 if err := w.AddRecursive(testDir); err != nil { 893 b.Fatal(err) 894 } 895 896 go func() { 897 // Start the watching process. 898 if err := w.Start(time.Millisecond); err != nil { 899 b.Fatal(err) 900 } 901 }() 902 903 var filenameFrom = filepath.Join("/"+testDir, "file.txt") 904 var filenameTo = filepath.Join("/"+testDir, "file1.txt") 905 906 for i := 0; i < b.N; i++ { 907 // Rename a file. 908 if err := os.Rename( 909 filenameFrom, 910 filenameTo, 911 ); err != nil { 912 b.Error(err) 913 } 914 915 select { 916 case event := <-w.Event: 917 if event.Op != Rename { 918 b.Errorf("expected event to be Rename, got %s", event.Op) 919 } 920 case <-time.After(time.Millisecond * 250): 921 b.Fatal("received no rename event") 922 } 923 924 filenameFrom, filenameTo = filenameTo, filenameFrom 925 } 926 } 927 928 func BenchmarkListFiles(b *testing.B) { 929 testDir, teardown := setup(b) 930 defer teardown() 931 932 w := New(os.DirFS("/")) 933 err := w.AddRecursive(testDir) 934 if err != nil { 935 b.Fatal(err) 936 } 937 938 for i := 0; i < b.N; i++ { 939 fileList := w.retrieveFileList() 940 if fileList == nil { 941 b.Fatal("expected file list to not be empty") 942 } 943 } 944 } 945 946 func TestClose(t *testing.T) { 947 testDir, teardown := setup(t) 948 defer teardown() 949 950 w := New(os.DirFS("/")) 951 952 err := w.Add(testDir) 953 if err != nil { 954 t.Fatal(err) 955 } 956 957 wf := w.WatchedFiles() 958 fileList := w.retrieveFileList() 959 960 if len(wf) != len(fileList) { 961 t.Fatalf("expected len of wf to be %d, got %d", len(fileList), len(wf)) 962 } 963 964 // Call close on the watcher even though it's not running. 965 w.Close() 966 967 wf = w.WatchedFiles() 968 fileList = w.retrieveFileList() 969 970 // Close will be a no-op so there will still be len(fileList) files. 971 if len(wf) != len(fileList) { 972 t.Fatalf("expected len of wf to be %d, got %d", len(fileList), len(wf)) 973 } 974 975 // Set running to true. 976 w.running = true 977 978 // Now close the watcher. 979 go func() { 980 // Receive from the w.close channel to avoid a deadlock. 981 <-w.close 982 }() 983 984 w.Close() 985 986 wf = w.WatchedFiles() 987 988 // Close will be a no-op so there will still be len(fileList) files. 989 if len(wf) != 0 { 990 t.Fatalf("expected len of wf to be 0, got %d", len(wf)) 991 } 992 993 } 994 995 func TestWatchedFiles(t *testing.T) { 996 testDir, teardown := setup(t) 997 defer teardown() 998 999 w := New(os.DirFS("/")) 1000 1001 err := w.Add(testDir) 1002 if err != nil { 1003 t.Fatal(err) 1004 } 1005 1006 wf := w.WatchedFiles() 1007 fileList := w.retrieveFileList() 1008 1009 if len(wf) != len(fileList) { 1010 t.Fatalf("expected len of wf to be %d, got %d", len(fileList), len(wf)) 1011 } 1012 1013 for path := range fileList { 1014 if _, found := wf[path]; !found { 1015 t.Fatalf("%s not found in watched file's list", path) 1016 } 1017 } 1018 } 1019 1020 func TestSetMaxEvents(t *testing.T) { 1021 w := New(os.DirFS("/")) 1022 1023 if w.maxEvents != 0 { 1024 t.Fatalf("expected max events to be 0, got %d", w.maxEvents) 1025 } 1026 1027 w.SetMaxEvents(3) 1028 1029 if w.maxEvents != 3 { 1030 t.Fatalf("expected max events to be 3, got %d", w.maxEvents) 1031 } 1032 } 1033 1034 func TestOpsString(t *testing.T) { 1035 testCases := []struct { 1036 want Op 1037 expected string 1038 }{ 1039 {Create, "CREATE"}, 1040 {Write, "WRITE"}, 1041 {Remove, "REMOVE"}, 1042 {Rename, "RENAME"}, 1043 {Chmod, "CHMOD"}, 1044 {Move, "MOVE"}, 1045 {Op(10), "???"}, 1046 } 1047 1048 for _, tc := range testCases { 1049 if tc.want.String() != tc.expected { 1050 t.Errorf("expected %s, got %s", tc.expected, tc.want.String()) 1051 } 1052 } 1053 }