1// Copyright 2010 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// +build !plan9,!solaris 6 7package fsnotify 8 9import ( 10 "io/ioutil" 11 "os" 12 "os/exec" 13 "path" 14 "path/filepath" 15 "runtime" 16 "sync/atomic" 17 "testing" 18 "time" 19) 20 21// An atomic counter 22type counter struct { 23 val int32 24} 25 26func (c *counter) increment() { 27 atomic.AddInt32(&c.val, 1) 28} 29 30func (c *counter) value() int32 { 31 return atomic.LoadInt32(&c.val) 32} 33 34func (c *counter) reset() { 35 atomic.StoreInt32(&c.val, 0) 36} 37 38// tempMkdir makes a temporary directory 39func tempMkdir(t *testing.T) string { 40 dir, err := ioutil.TempDir("", "fsnotify") 41 if err != nil { 42 t.Fatalf("failed to create test directory: %s", err) 43 } 44 return dir 45} 46 47// tempMkFile makes a temporary file. 48func tempMkFile(t *testing.T, dir string) string { 49 f, err := ioutil.TempFile(dir, "fsnotify") 50 if err != nil { 51 t.Fatalf("failed to create test file: %v", err) 52 } 53 defer f.Close() 54 return f.Name() 55} 56 57// newWatcher initializes an fsnotify Watcher instance. 58func newWatcher(t *testing.T) *Watcher { 59 watcher, err := NewWatcher() 60 if err != nil { 61 t.Fatalf("NewWatcher() failed: %s", err) 62 } 63 return watcher 64} 65 66// addWatch adds a watch for a directory 67func addWatch(t *testing.T, watcher *Watcher, dir string) { 68 if err := watcher.Add(dir); err != nil { 69 t.Fatalf("watcher.Add(%q) failed: %s", dir, err) 70 } 71} 72 73func TestFsnotifyMultipleOperations(t *testing.T) { 74 watcher := newWatcher(t) 75 76 // Receive errors on the error channel on a separate goroutine 77 go func() { 78 for err := range watcher.Errors { 79 t.Fatalf("error received: %s", err) 80 } 81 }() 82 83 // Create directory to watch 84 testDir := tempMkdir(t) 85 defer os.RemoveAll(testDir) 86 87 // Create directory that's not watched 88 testDirToMoveFiles := tempMkdir(t) 89 defer os.RemoveAll(testDirToMoveFiles) 90 91 testFile := filepath.Join(testDir, "TestFsnotifySeq.testfile") 92 testFileRenamed := filepath.Join(testDirToMoveFiles, "TestFsnotifySeqRename.testfile") 93 94 addWatch(t, watcher, testDir) 95 96 // Receive events on the event channel on a separate goroutine 97 eventstream := watcher.Events 98 var createReceived, modifyReceived, deleteReceived, renameReceived counter 99 done := make(chan bool) 100 go func() { 101 for event := range eventstream { 102 // Only count relevant events 103 if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) { 104 t.Logf("event received: %s", event) 105 if event.Op&Remove == Remove { 106 deleteReceived.increment() 107 } 108 if event.Op&Write == Write { 109 modifyReceived.increment() 110 } 111 if event.Op&Create == Create { 112 createReceived.increment() 113 } 114 if event.Op&Rename == Rename { 115 renameReceived.increment() 116 } 117 } else { 118 t.Logf("unexpected event received: %s", event) 119 } 120 } 121 done <- true 122 }() 123 124 // Create a file 125 // This should add at least one event to the fsnotify event queue 126 var f *os.File 127 f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) 128 if err != nil { 129 t.Fatalf("creating test file failed: %s", err) 130 } 131 f.Sync() 132 133 time.Sleep(time.Millisecond) 134 f.WriteString("data") 135 f.Sync() 136 f.Close() 137 138 time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete 139 140 if err := testRename(testFile, testFileRenamed); err != nil { 141 t.Fatalf("rename failed: %s", err) 142 } 143 144 // Modify the file outside of the watched dir 145 f, err = os.Open(testFileRenamed) 146 if err != nil { 147 t.Fatalf("open test renamed file failed: %s", err) 148 } 149 f.WriteString("data") 150 f.Sync() 151 f.Close() 152 153 time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete 154 155 // Recreate the file that was moved 156 f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) 157 if err != nil { 158 t.Fatalf("creating test file failed: %s", err) 159 } 160 f.Close() 161 time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete 162 163 // We expect this event to be received almost immediately, but let's wait 500 ms to be sure 164 time.Sleep(500 * time.Millisecond) 165 cReceived := createReceived.value() 166 if cReceived != 2 { 167 t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2) 168 } 169 mReceived := modifyReceived.value() 170 if mReceived != 1 { 171 t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", mReceived, 1) 172 } 173 dReceived := deleteReceived.value() 174 rReceived := renameReceived.value() 175 if dReceived+rReceived != 1 { 176 t.Fatalf("incorrect number of rename+delete events received after 500 ms (%d vs %d)", rReceived+dReceived, 1) 177 } 178 179 // Try closing the fsnotify instance 180 t.Log("calling Close()") 181 watcher.Close() 182 t.Log("waiting for the event channel to become closed...") 183 select { 184 case <-done: 185 t.Log("event channel closed") 186 case <-time.After(2 * time.Second): 187 t.Fatal("event stream was not closed after 2 seconds") 188 } 189} 190 191func TestFsnotifyMultipleCreates(t *testing.T) { 192 watcher := newWatcher(t) 193 194 // Receive errors on the error channel on a separate goroutine 195 go func() { 196 for err := range watcher.Errors { 197 t.Errorf("error received: %s", err) 198 } 199 }() 200 201 // Create directory to watch 202 testDir := tempMkdir(t) 203 defer os.RemoveAll(testDir) 204 205 testFile := filepath.Join(testDir, "TestFsnotifySeq.testfile") 206 207 addWatch(t, watcher, testDir) 208 209 // Receive events on the event channel on a separate goroutine 210 eventstream := watcher.Events 211 var createReceived, modifyReceived, deleteReceived counter 212 done := make(chan bool) 213 go func() { 214 for event := range eventstream { 215 // Only count relevant events 216 if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) { 217 t.Logf("event received: %s", event) 218 if event.Op&Remove == Remove { 219 deleteReceived.increment() 220 } 221 if event.Op&Create == Create { 222 createReceived.increment() 223 } 224 if event.Op&Write == Write { 225 modifyReceived.increment() 226 } 227 } else { 228 t.Logf("unexpected event received: %s", event) 229 } 230 } 231 done <- true 232 }() 233 234 // Create a file 235 // This should add at least one event to the fsnotify event queue 236 var f *os.File 237 f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) 238 if err != nil { 239 t.Fatalf("creating test file failed: %s", err) 240 } 241 f.Sync() 242 243 time.Sleep(time.Millisecond) 244 f.WriteString("data") 245 f.Sync() 246 f.Close() 247 248 time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete 249 250 os.Remove(testFile) 251 252 time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete 253 254 // Recreate the file 255 f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) 256 if err != nil { 257 t.Fatalf("creating test file failed: %s", err) 258 } 259 f.Close() 260 time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete 261 262 // Modify 263 f, err = os.OpenFile(testFile, os.O_WRONLY, 0666) 264 if err != nil { 265 t.Fatalf("creating test file failed: %s", err) 266 } 267 f.Sync() 268 269 time.Sleep(time.Millisecond) 270 f.WriteString("data") 271 f.Sync() 272 f.Close() 273 274 time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete 275 276 // Modify 277 f, err = os.OpenFile(testFile, os.O_WRONLY, 0666) 278 if err != nil { 279 t.Fatalf("creating test file failed: %s", err) 280 } 281 f.Sync() 282 283 time.Sleep(time.Millisecond) 284 f.WriteString("data") 285 f.Sync() 286 f.Close() 287 288 time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete 289 290 // We expect this event to be received almost immediately, but let's wait 500 ms to be sure 291 time.Sleep(500 * time.Millisecond) 292 cReceived := createReceived.value() 293 if cReceived != 2 { 294 t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2) 295 } 296 mReceived := modifyReceived.value() 297 if mReceived < 3 { 298 t.Fatalf("incorrect number of modify events received after 500 ms (%d vs atleast %d)", mReceived, 3) 299 } 300 dReceived := deleteReceived.value() 301 if dReceived != 1 { 302 t.Fatalf("incorrect number of rename+delete events received after 500 ms (%d vs %d)", dReceived, 1) 303 } 304 305 // Try closing the fsnotify instance 306 t.Log("calling Close()") 307 watcher.Close() 308 t.Log("waiting for the event channel to become closed...") 309 select { 310 case <-done: 311 t.Log("event channel closed") 312 case <-time.After(2 * time.Second): 313 t.Fatal("event stream was not closed after 2 seconds") 314 } 315} 316 317func TestFsnotifyDirOnly(t *testing.T) { 318 watcher := newWatcher(t) 319 320 // Create directory to watch 321 testDir := tempMkdir(t) 322 defer os.RemoveAll(testDir) 323 324 // Create a file before watching directory 325 // This should NOT add any events to the fsnotify event queue 326 testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile") 327 { 328 var f *os.File 329 f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666) 330 if err != nil { 331 t.Fatalf("creating test file failed: %s", err) 332 } 333 f.Sync() 334 f.Close() 335 } 336 337 addWatch(t, watcher, testDir) 338 339 // Receive errors on the error channel on a separate goroutine 340 go func() { 341 for err := range watcher.Errors { 342 t.Errorf("error received: %s", err) 343 } 344 }() 345 346 testFile := filepath.Join(testDir, "TestFsnotifyDirOnly.testfile") 347 348 // Receive events on the event channel on a separate goroutine 349 eventstream := watcher.Events 350 var createReceived, modifyReceived, deleteReceived counter 351 done := make(chan bool) 352 go func() { 353 for event := range eventstream { 354 // Only count relevant events 355 if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileAlreadyExists) { 356 t.Logf("event received: %s", event) 357 if event.Op&Remove == Remove { 358 deleteReceived.increment() 359 } 360 if event.Op&Write == Write { 361 modifyReceived.increment() 362 } 363 if event.Op&Create == Create { 364 createReceived.increment() 365 } 366 } else { 367 t.Logf("unexpected event received: %s", event) 368 } 369 } 370 done <- true 371 }() 372 373 // Create a file 374 // This should add at least one event to the fsnotify event queue 375 var f *os.File 376 f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) 377 if err != nil { 378 t.Fatalf("creating test file failed: %s", err) 379 } 380 f.Sync() 381 382 time.Sleep(time.Millisecond) 383 f.WriteString("data") 384 f.Sync() 385 f.Close() 386 387 time.Sleep(50 * time.Millisecond) // give system time to sync write change before delete 388 389 os.Remove(testFile) 390 os.Remove(testFileAlreadyExists) 391 392 // We expect this event to be received almost immediately, but let's wait 500 ms to be sure 393 time.Sleep(500 * time.Millisecond) 394 cReceived := createReceived.value() 395 if cReceived != 1 { 396 t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 1) 397 } 398 mReceived := modifyReceived.value() 399 if mReceived != 1 { 400 t.Fatalf("incorrect number of modify events received after 500 ms (%d vs %d)", mReceived, 1) 401 } 402 dReceived := deleteReceived.value() 403 if dReceived != 2 { 404 t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", dReceived, 2) 405 } 406 407 // Try closing the fsnotify instance 408 t.Log("calling Close()") 409 watcher.Close() 410 t.Log("waiting for the event channel to become closed...") 411 select { 412 case <-done: 413 t.Log("event channel closed") 414 case <-time.After(2 * time.Second): 415 t.Fatal("event stream was not closed after 2 seconds") 416 } 417} 418 419func TestFsnotifyDeleteWatchedDir(t *testing.T) { 420 watcher := newWatcher(t) 421 defer watcher.Close() 422 423 // Create directory to watch 424 testDir := tempMkdir(t) 425 defer os.RemoveAll(testDir) 426 427 // Create a file before watching directory 428 testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile") 429 { 430 var f *os.File 431 f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666) 432 if err != nil { 433 t.Fatalf("creating test file failed: %s", err) 434 } 435 f.Sync() 436 f.Close() 437 } 438 439 addWatch(t, watcher, testDir) 440 441 // Add a watch for testFile 442 addWatch(t, watcher, testFileAlreadyExists) 443 444 // Receive errors on the error channel on a separate goroutine 445 go func() { 446 for err := range watcher.Errors { 447 t.Errorf("error received: %s", err) 448 } 449 }() 450 451 // Receive events on the event channel on a separate goroutine 452 eventstream := watcher.Events 453 var deleteReceived counter 454 go func() { 455 for event := range eventstream { 456 // Only count relevant events 457 if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFileAlreadyExists) { 458 t.Logf("event received: %s", event) 459 if event.Op&Remove == Remove { 460 deleteReceived.increment() 461 } 462 } else { 463 t.Logf("unexpected event received: %s", event) 464 } 465 } 466 }() 467 468 os.RemoveAll(testDir) 469 470 // We expect this event to be received almost immediately, but let's wait 500 ms to be sure 471 time.Sleep(500 * time.Millisecond) 472 dReceived := deleteReceived.value() 473 if dReceived < 2 { 474 t.Fatalf("did not receive at least %d delete events, received %d after 500 ms", 2, dReceived) 475 } 476} 477 478func TestFsnotifySubDir(t *testing.T) { 479 watcher := newWatcher(t) 480 481 // Create directory to watch 482 testDir := tempMkdir(t) 483 defer os.RemoveAll(testDir) 484 485 testFile1 := filepath.Join(testDir, "TestFsnotifyFile1.testfile") 486 testSubDir := filepath.Join(testDir, "sub") 487 testSubDirFile := filepath.Join(testDir, "sub/TestFsnotifyFile1.testfile") 488 489 // Receive errors on the error channel on a separate goroutine 490 go func() { 491 for err := range watcher.Errors { 492 t.Errorf("error received: %s", err) 493 } 494 }() 495 496 // Receive events on the event channel on a separate goroutine 497 eventstream := watcher.Events 498 var createReceived, deleteReceived counter 499 done := make(chan bool) 500 go func() { 501 for event := range eventstream { 502 // Only count relevant events 503 if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testSubDir) || event.Name == filepath.Clean(testFile1) { 504 t.Logf("event received: %s", event) 505 if event.Op&Create == Create { 506 createReceived.increment() 507 } 508 if event.Op&Remove == Remove { 509 deleteReceived.increment() 510 } 511 } else { 512 t.Logf("unexpected event received: %s", event) 513 } 514 } 515 done <- true 516 }() 517 518 addWatch(t, watcher, testDir) 519 520 // Create sub-directory 521 if err := os.Mkdir(testSubDir, 0777); err != nil { 522 t.Fatalf("failed to create test sub-directory: %s", err) 523 } 524 525 // Create a file 526 var f *os.File 527 f, err := os.OpenFile(testFile1, os.O_WRONLY|os.O_CREATE, 0666) 528 if err != nil { 529 t.Fatalf("creating test file failed: %s", err) 530 } 531 f.Sync() 532 f.Close() 533 534 // Create a file (Should not see this! we are not watching subdir) 535 var fs *os.File 536 fs, err = os.OpenFile(testSubDirFile, os.O_WRONLY|os.O_CREATE, 0666) 537 if err != nil { 538 t.Fatalf("creating test file failed: %s", err) 539 } 540 fs.Sync() 541 fs.Close() 542 543 time.Sleep(200 * time.Millisecond) 544 545 // Make sure receive deletes for both file and sub-directory 546 os.RemoveAll(testSubDir) 547 os.Remove(testFile1) 548 549 // We expect this event to be received almost immediately, but let's wait 500 ms to be sure 550 time.Sleep(500 * time.Millisecond) 551 cReceived := createReceived.value() 552 if cReceived != 2 { 553 t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", cReceived, 2) 554 } 555 dReceived := deleteReceived.value() 556 if dReceived != 2 { 557 t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", dReceived, 2) 558 } 559 560 // Try closing the fsnotify instance 561 t.Log("calling Close()") 562 watcher.Close() 563 t.Log("waiting for the event channel to become closed...") 564 select { 565 case <-done: 566 t.Log("event channel closed") 567 case <-time.After(2 * time.Second): 568 t.Fatal("event stream was not closed after 2 seconds") 569 } 570} 571 572func TestFsnotifyRename(t *testing.T) { 573 watcher := newWatcher(t) 574 575 // Create directory to watch 576 testDir := tempMkdir(t) 577 defer os.RemoveAll(testDir) 578 579 addWatch(t, watcher, testDir) 580 581 // Receive errors on the error channel on a separate goroutine 582 go func() { 583 for err := range watcher.Errors { 584 t.Errorf("error received: %s", err) 585 } 586 }() 587 588 testFile := filepath.Join(testDir, "TestFsnotifyEvents.testfile") 589 testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed") 590 591 // Receive events on the event channel on a separate goroutine 592 eventstream := watcher.Events 593 var renameReceived counter 594 done := make(chan bool) 595 go func() { 596 for event := range eventstream { 597 // Only count relevant events 598 if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileRenamed) { 599 if event.Op&Rename == Rename { 600 renameReceived.increment() 601 } 602 t.Logf("event received: %s", event) 603 } else { 604 t.Logf("unexpected event received: %s", event) 605 } 606 } 607 done <- true 608 }() 609 610 // Create a file 611 // This should add at least one event to the fsnotify event queue 612 var f *os.File 613 f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) 614 if err != nil { 615 t.Fatalf("creating test file failed: %s", err) 616 } 617 f.Sync() 618 619 f.WriteString("data") 620 f.Sync() 621 f.Close() 622 623 // Add a watch for testFile 624 addWatch(t, watcher, testFile) 625 626 if err := testRename(testFile, testFileRenamed); err != nil { 627 t.Fatalf("rename failed: %s", err) 628 } 629 630 // We expect this event to be received almost immediately, but let's wait 500 ms to be sure 631 time.Sleep(500 * time.Millisecond) 632 if renameReceived.value() == 0 { 633 t.Fatal("fsnotify rename events have not been received after 500 ms") 634 } 635 636 // Try closing the fsnotify instance 637 t.Log("calling Close()") 638 watcher.Close() 639 t.Log("waiting for the event channel to become closed...") 640 select { 641 case <-done: 642 t.Log("event channel closed") 643 case <-time.After(2 * time.Second): 644 t.Fatal("event stream was not closed after 2 seconds") 645 } 646 647 os.Remove(testFileRenamed) 648} 649 650func TestFsnotifyRenameToCreate(t *testing.T) { 651 watcher := newWatcher(t) 652 653 // Create directory to watch 654 testDir := tempMkdir(t) 655 defer os.RemoveAll(testDir) 656 657 // Create directory to get file 658 testDirFrom := tempMkdir(t) 659 defer os.RemoveAll(testDirFrom) 660 661 addWatch(t, watcher, testDir) 662 663 // Receive errors on the error channel on a separate goroutine 664 go func() { 665 for err := range watcher.Errors { 666 t.Errorf("error received: %s", err) 667 } 668 }() 669 670 testFile := filepath.Join(testDirFrom, "TestFsnotifyEvents.testfile") 671 testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed") 672 673 // Receive events on the event channel on a separate goroutine 674 eventstream := watcher.Events 675 var createReceived counter 676 done := make(chan bool) 677 go func() { 678 for event := range eventstream { 679 // Only count relevant events 680 if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) || event.Name == filepath.Clean(testFileRenamed) { 681 if event.Op&Create == Create { 682 createReceived.increment() 683 } 684 t.Logf("event received: %s", event) 685 } else { 686 t.Logf("unexpected event received: %s", event) 687 } 688 } 689 done <- true 690 }() 691 692 // Create a file 693 // This should add at least one event to the fsnotify event queue 694 var f *os.File 695 f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) 696 if err != nil { 697 t.Fatalf("creating test file failed: %s", err) 698 } 699 f.Sync() 700 f.Close() 701 702 if err := testRename(testFile, testFileRenamed); err != nil { 703 t.Fatalf("rename failed: %s", err) 704 } 705 706 // We expect this event to be received almost immediately, but let's wait 500 ms to be sure 707 time.Sleep(500 * time.Millisecond) 708 if createReceived.value() == 0 { 709 t.Fatal("fsnotify create events have not been received after 500 ms") 710 } 711 712 // Try closing the fsnotify instance 713 t.Log("calling Close()") 714 watcher.Close() 715 t.Log("waiting for the event channel to become closed...") 716 select { 717 case <-done: 718 t.Log("event channel closed") 719 case <-time.After(2 * time.Second): 720 t.Fatal("event stream was not closed after 2 seconds") 721 } 722 723 os.Remove(testFileRenamed) 724} 725 726func TestFsnotifyRenameToOverwrite(t *testing.T) { 727 switch runtime.GOOS { 728 case "plan9", "windows": 729 t.Skipf("skipping test on %q (os.Rename over existing file does not create event).", runtime.GOOS) 730 } 731 732 watcher := newWatcher(t) 733 734 // Create directory to watch 735 testDir := tempMkdir(t) 736 defer os.RemoveAll(testDir) 737 738 // Create directory to get file 739 testDirFrom := tempMkdir(t) 740 defer os.RemoveAll(testDirFrom) 741 742 testFile := filepath.Join(testDirFrom, "TestFsnotifyEvents.testfile") 743 testFileRenamed := filepath.Join(testDir, "TestFsnotifyEvents.testfileRenamed") 744 745 // Create a file 746 var fr *os.File 747 fr, err := os.OpenFile(testFileRenamed, os.O_WRONLY|os.O_CREATE, 0666) 748 if err != nil { 749 t.Fatalf("creating test file failed: %s", err) 750 } 751 fr.Sync() 752 fr.Close() 753 754 addWatch(t, watcher, testDir) 755 756 // Receive errors on the error channel on a separate goroutine 757 go func() { 758 for err := range watcher.Errors { 759 t.Errorf("error received: %s", err) 760 } 761 }() 762 763 // Receive events on the event channel on a separate goroutine 764 eventstream := watcher.Events 765 var eventReceived counter 766 done := make(chan bool) 767 go func() { 768 for event := range eventstream { 769 // Only count relevant events 770 if event.Name == filepath.Clean(testFileRenamed) { 771 eventReceived.increment() 772 t.Logf("event received: %s", event) 773 } else { 774 t.Logf("unexpected event received: %s", event) 775 } 776 } 777 done <- true 778 }() 779 780 // Create a file 781 // This should add at least one event to the fsnotify event queue 782 var f *os.File 783 f, err = os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) 784 if err != nil { 785 t.Fatalf("creating test file failed: %s", err) 786 } 787 f.Sync() 788 f.Close() 789 790 if err := testRename(testFile, testFileRenamed); err != nil { 791 t.Fatalf("rename failed: %s", err) 792 } 793 794 // We expect this event to be received almost immediately, but let's wait 500 ms to be sure 795 time.Sleep(500 * time.Millisecond) 796 if eventReceived.value() == 0 { 797 t.Fatal("fsnotify events have not been received after 500 ms") 798 } 799 800 // Try closing the fsnotify instance 801 t.Log("calling Close()") 802 watcher.Close() 803 t.Log("waiting for the event channel to become closed...") 804 select { 805 case <-done: 806 t.Log("event channel closed") 807 case <-time.After(2 * time.Second): 808 t.Fatal("event stream was not closed after 2 seconds") 809 } 810 811 os.Remove(testFileRenamed) 812} 813 814func TestRemovalOfWatch(t *testing.T) { 815 // Create directory to watch 816 testDir := tempMkdir(t) 817 defer os.RemoveAll(testDir) 818 819 // Create a file before watching directory 820 testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile") 821 { 822 var f *os.File 823 f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666) 824 if err != nil { 825 t.Fatalf("creating test file failed: %s", err) 826 } 827 f.Sync() 828 f.Close() 829 } 830 831 watcher := newWatcher(t) 832 defer watcher.Close() 833 834 addWatch(t, watcher, testDir) 835 if err := watcher.Remove(testDir); err != nil { 836 t.Fatalf("Could not remove the watch: %v\n", err) 837 } 838 839 go func() { 840 select { 841 case ev := <-watcher.Events: 842 t.Fatalf("We received event: %v\n", ev) 843 case <-time.After(500 * time.Millisecond): 844 t.Log("No event received, as expected.") 845 } 846 }() 847 848 time.Sleep(200 * time.Millisecond) 849 // Modify the file outside of the watched dir 850 f, err := os.Open(testFileAlreadyExists) 851 if err != nil { 852 t.Fatalf("Open test file failed: %s", err) 853 } 854 f.WriteString("data") 855 f.Sync() 856 f.Close() 857 if err := os.Chmod(testFileAlreadyExists, 0700); err != nil { 858 t.Fatalf("chmod failed: %s", err) 859 } 860 time.Sleep(400 * time.Millisecond) 861} 862 863func TestFsnotifyAttrib(t *testing.T) { 864 if runtime.GOOS == "windows" { 865 t.Skip("attributes don't work on Windows.") 866 } 867 868 watcher := newWatcher(t) 869 870 // Create directory to watch 871 testDir := tempMkdir(t) 872 defer os.RemoveAll(testDir) 873 874 // Receive errors on the error channel on a separate goroutine 875 go func() { 876 for err := range watcher.Errors { 877 t.Errorf("error received: %s", err) 878 } 879 }() 880 881 testFile := filepath.Join(testDir, "TestFsnotifyAttrib.testfile") 882 883 // Receive events on the event channel on a separate goroutine 884 eventstream := watcher.Events 885 // The modifyReceived counter counts IsModify events that are not IsAttrib, 886 // and the attribReceived counts IsAttrib events (which are also IsModify as 887 // a consequence). 888 var modifyReceived counter 889 var attribReceived counter 890 done := make(chan bool) 891 go func() { 892 for event := range eventstream { 893 // Only count relevant events 894 if event.Name == filepath.Clean(testDir) || event.Name == filepath.Clean(testFile) { 895 if event.Op&Write == Write { 896 modifyReceived.increment() 897 } 898 if event.Op&Chmod == Chmod { 899 attribReceived.increment() 900 } 901 t.Logf("event received: %s", event) 902 } else { 903 t.Logf("unexpected event received: %s", event) 904 } 905 } 906 done <- true 907 }() 908 909 // Create a file 910 // This should add at least one event to the fsnotify event queue 911 var f *os.File 912 f, err := os.OpenFile(testFile, os.O_WRONLY|os.O_CREATE, 0666) 913 if err != nil { 914 t.Fatalf("creating test file failed: %s", err) 915 } 916 f.Sync() 917 918 f.WriteString("data") 919 f.Sync() 920 f.Close() 921 922 // Add a watch for testFile 923 addWatch(t, watcher, testFile) 924 925 if err := os.Chmod(testFile, 0700); err != nil { 926 t.Fatalf("chmod failed: %s", err) 927 } 928 929 // We expect this event to be received almost immediately, but let's wait 500 ms to be sure 930 // Creating/writing a file changes also the mtime, so IsAttrib should be set to true here 931 time.Sleep(500 * time.Millisecond) 932 if modifyReceived.value() != 0 { 933 t.Fatal("received an unexpected modify event when creating a test file") 934 } 935 if attribReceived.value() == 0 { 936 t.Fatal("fsnotify attribute events have not received after 500 ms") 937 } 938 939 // Modifying the contents of the file does not set the attrib flag (although eg. the mtime 940 // might have been modified). 941 modifyReceived.reset() 942 attribReceived.reset() 943 944 f, err = os.OpenFile(testFile, os.O_WRONLY, 0) 945 if err != nil { 946 t.Fatalf("reopening test file failed: %s", err) 947 } 948 949 f.WriteString("more data") 950 f.Sync() 951 f.Close() 952 953 time.Sleep(500 * time.Millisecond) 954 955 if modifyReceived.value() != 1 { 956 t.Fatal("didn't receive a modify event after changing test file contents") 957 } 958 959 if attribReceived.value() != 0 { 960 t.Fatal("did receive an unexpected attrib event after changing test file contents") 961 } 962 963 modifyReceived.reset() 964 attribReceived.reset() 965 966 // Doing a chmod on the file should trigger an event with the "attrib" flag set (the contents 967 // of the file are not changed though) 968 if err := os.Chmod(testFile, 0600); err != nil { 969 t.Fatalf("chmod failed: %s", err) 970 } 971 972 time.Sleep(500 * time.Millisecond) 973 974 if attribReceived.value() != 1 { 975 t.Fatal("didn't receive an attribute change after 500ms") 976 } 977 978 // Try closing the fsnotify instance 979 t.Log("calling Close()") 980 watcher.Close() 981 t.Log("waiting for the event channel to become closed...") 982 select { 983 case <-done: 984 t.Log("event channel closed") 985 case <-time.After(1e9): 986 t.Fatal("event stream was not closed after 1 second") 987 } 988 989 os.Remove(testFile) 990} 991 992func TestFsnotifyClose(t *testing.T) { 993 watcher := newWatcher(t) 994 watcher.Close() 995 996 var done int32 997 go func() { 998 watcher.Close() 999 atomic.StoreInt32(&done, 1) 1000 }() 1001 1002 time.Sleep(50e6) // 50 ms 1003 if atomic.LoadInt32(&done) == 0 { 1004 t.Fatal("double Close() test failed: second Close() call didn't return") 1005 } 1006 1007 testDir := tempMkdir(t) 1008 defer os.RemoveAll(testDir) 1009 1010 if err := watcher.Add(testDir); err == nil { 1011 t.Fatal("expected error on Watch() after Close(), got nil") 1012 } 1013} 1014 1015func TestFsnotifyFakeSymlink(t *testing.T) { 1016 if runtime.GOOS == "windows" { 1017 t.Skip("symlinks don't work on Windows.") 1018 } 1019 1020 watcher := newWatcher(t) 1021 1022 // Create directory to watch 1023 testDir := tempMkdir(t) 1024 defer os.RemoveAll(testDir) 1025 1026 var errorsReceived counter 1027 // Receive errors on the error channel on a separate goroutine 1028 go func() { 1029 for errors := range watcher.Errors { 1030 t.Logf("Received error: %s", errors) 1031 errorsReceived.increment() 1032 } 1033 }() 1034 1035 // Count the CREATE events received 1036 var createEventsReceived, otherEventsReceived counter 1037 go func() { 1038 for ev := range watcher.Events { 1039 t.Logf("event received: %s", ev) 1040 if ev.Op&Create == Create { 1041 createEventsReceived.increment() 1042 } else { 1043 otherEventsReceived.increment() 1044 } 1045 } 1046 }() 1047 1048 addWatch(t, watcher, testDir) 1049 1050 if err := os.Symlink(filepath.Join(testDir, "zzz"), filepath.Join(testDir, "zzznew")); err != nil { 1051 t.Fatalf("Failed to create bogus symlink: %s", err) 1052 } 1053 t.Logf("Created bogus symlink") 1054 1055 // We expect this event to be received almost immediately, but let's wait 500 ms to be sure 1056 time.Sleep(500 * time.Millisecond) 1057 1058 // Should not be error, just no events for broken links (watching nothing) 1059 if errorsReceived.value() > 0 { 1060 t.Fatal("fsnotify errors have been received.") 1061 } 1062 if otherEventsReceived.value() > 0 { 1063 t.Fatal("fsnotify other events received on the broken link") 1064 } 1065 1066 // Except for 1 create event (for the link itself) 1067 if createEventsReceived.value() == 0 { 1068 t.Fatal("fsnotify create events were not received after 500 ms") 1069 } 1070 if createEventsReceived.value() > 1 { 1071 t.Fatal("fsnotify more create events received than expected") 1072 } 1073 1074 // Try closing the fsnotify instance 1075 t.Log("calling Close()") 1076 watcher.Close() 1077} 1078 1079func TestCyclicSymlink(t *testing.T) { 1080 if runtime.GOOS == "windows" { 1081 t.Skip("symlinks don't work on Windows.") 1082 } 1083 1084 watcher := newWatcher(t) 1085 1086 testDir := tempMkdir(t) 1087 defer os.RemoveAll(testDir) 1088 1089 link := path.Join(testDir, "link") 1090 if err := os.Symlink(".", link); err != nil { 1091 t.Fatalf("could not make symlink: %v", err) 1092 } 1093 addWatch(t, watcher, testDir) 1094 1095 var createEventsReceived counter 1096 go func() { 1097 for ev := range watcher.Events { 1098 if ev.Op&Create == Create { 1099 createEventsReceived.increment() 1100 } 1101 } 1102 }() 1103 1104 if err := os.Remove(link); err != nil { 1105 t.Fatalf("Error removing link: %v", err) 1106 } 1107 1108 // It would be nice to be able to expect a delete event here, but kqueue has 1109 // no way for us to get events on symlinks themselves, because opening them 1110 // opens an fd to the file to which they point. 1111 1112 if err := ioutil.WriteFile(link, []byte("foo"), 0700); err != nil { 1113 t.Fatalf("could not make symlink: %v", err) 1114 } 1115 1116 // We expect this event to be received almost immediately, but let's wait 500 ms to be sure 1117 time.Sleep(500 * time.Millisecond) 1118 1119 if got := createEventsReceived.value(); got == 0 { 1120 t.Errorf("want at least 1 create event got %v", got) 1121 } 1122 1123 watcher.Close() 1124} 1125 1126// TestConcurrentRemovalOfWatch tests that concurrent calls to RemoveWatch do not race. 1127// See https://codereview.appspot.com/103300045/ 1128// go test -test.run=TestConcurrentRemovalOfWatch -test.cpu=1,1,1,1,1 -race 1129func TestConcurrentRemovalOfWatch(t *testing.T) { 1130 if runtime.GOOS != "darwin" { 1131 t.Skip("regression test for race only present on darwin") 1132 } 1133 1134 // Create directory to watch 1135 testDir := tempMkdir(t) 1136 defer os.RemoveAll(testDir) 1137 1138 // Create a file before watching directory 1139 testFileAlreadyExists := filepath.Join(testDir, "TestFsnotifyEventsExisting.testfile") 1140 { 1141 var f *os.File 1142 f, err := os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666) 1143 if err != nil { 1144 t.Fatalf("creating test file failed: %s", err) 1145 } 1146 f.Sync() 1147 f.Close() 1148 } 1149 1150 watcher := newWatcher(t) 1151 defer watcher.Close() 1152 1153 addWatch(t, watcher, testDir) 1154 1155 // Test that RemoveWatch can be invoked concurrently, with no data races. 1156 removed1 := make(chan struct{}) 1157 go func() { 1158 defer close(removed1) 1159 watcher.Remove(testDir) 1160 }() 1161 removed2 := make(chan struct{}) 1162 go func() { 1163 close(removed2) 1164 watcher.Remove(testDir) 1165 }() 1166 <-removed1 1167 <-removed2 1168} 1169 1170func TestClose(t *testing.T) { 1171 // Regression test for #59 bad file descriptor from Close 1172 testDir := tempMkdir(t) 1173 defer os.RemoveAll(testDir) 1174 1175 watcher := newWatcher(t) 1176 if err := watcher.Add(testDir); err != nil { 1177 t.Fatalf("Expected no error on Add, got %v", err) 1178 } 1179 err := watcher.Close() 1180 if err != nil { 1181 t.Fatalf("Expected no error on Close, got %v.", err) 1182 } 1183} 1184 1185// TestRemoveWithClose tests if one can handle Remove events and, at the same 1186// time, close Watcher object without any data races. 1187func TestRemoveWithClose(t *testing.T) { 1188 testDir := tempMkdir(t) 1189 defer os.RemoveAll(testDir) 1190 1191 const fileN = 200 1192 tempFiles := make([]string, 0, fileN) 1193 for i := 0; i < fileN; i++ { 1194 tempFiles = append(tempFiles, tempMkFile(t, testDir)) 1195 } 1196 watcher := newWatcher(t) 1197 if err := watcher.Add(testDir); err != nil { 1198 t.Fatalf("Expected no error on Add, got %v", err) 1199 } 1200 startC, stopC := make(chan struct{}), make(chan struct{}) 1201 errC := make(chan error) 1202 go func() { 1203 for { 1204 select { 1205 case <-watcher.Errors: 1206 case <-watcher.Events: 1207 case <-stopC: 1208 return 1209 } 1210 } 1211 }() 1212 go func() { 1213 <-startC 1214 for _, fileName := range tempFiles { 1215 os.Remove(fileName) 1216 } 1217 }() 1218 go func() { 1219 <-startC 1220 errC <- watcher.Close() 1221 }() 1222 close(startC) 1223 defer close(stopC) 1224 if err := <-errC; err != nil { 1225 t.Fatalf("Expected no error on Close, got %v.", err) 1226 } 1227} 1228 1229func testRename(file1, file2 string) error { 1230 switch runtime.GOOS { 1231 case "windows", "plan9": 1232 return os.Rename(file1, file2) 1233 default: 1234 cmd := exec.Command("mv", file1, file2) 1235 return cmd.Run() 1236 } 1237} 1238