]> go.fuhry.dev Git - fsnotify.git/commitdiff
BSD - Fix for Issue #24 - Deleting watched directory
authorChris Howey <chris@howey.me>
Thu, 1 Nov 2012 12:12:29 +0000 (07:12 -0500)
committerChris Howey <chris@howey.me>
Thu, 1 Nov 2012 12:12:29 +0000 (07:12 -0500)
This fixes the issue where the delete of a watched directory sends and
error instead of a delete for the directory and the underlying watched
files.

The reproduce case for reference: https://gist.github.com/3972447

Also included is a test case addition for the above issue.

fsnotify_bsd.go
fsnotify_test.go

index 0dc7bad39bf09110b2d898bd7786dfaeb607d5c0..fd66c5bfc940768f40effc66a7320c0a616ab055 100644 (file)
@@ -317,7 +317,21 @@ func (w *Watcher) sendDirectoryChangeEvents(dirPath string) {
        // Get all files
        files, err := ioutil.ReadDir(dirPath)
        if err != nil {
-               w.Error <- err
+               // If the directory does not exist, we should pass on a delete event.
+               // We will likely not receive the OS delete event as this library is 
+               // holding an open file handle on the directory and it may not be 
+               // considered deleted until we release it. But once we release it, 
+               // we will no longer be watching it.
+               if _, found := w.watches[dirPath]; found && os.IsNotExist(err) {
+                       fileEvent := new(FileEvent)
+                       fileEvent.Name = dirPath
+                       fileEvent.mask = NOTE_DELETE
+                       w.internalEvent <- fileEvent
+                       w.removeWatch(dirPath)
+                       return
+               } else {
+                       w.Error <- err
+               }
        }
 
        // Search for new files
index 929b523e1f445c32798700fa6c79fee9857b7ce1..a8ee9a6cdf57d9defe30e1b2d4cce7524f87a197 100644 (file)
@@ -393,6 +393,90 @@ func TestFsnotifyDirOnly(t *testing.T) {
        }
 }
 
+func TestFsnotifyDeleteWatchedDir(t *testing.T) {
+       // Create an fsnotify watcher instance and initialize it
+       watcher, err := NewWatcher()
+       if err != nil {
+               t.Fatalf("NewWatcher() failed: %s", err)
+       }
+
+       const testDir string = "_test"
+
+       // Create directory to watch
+       if os.Mkdir(testDir, 0777) != nil {
+               t.Fatalf("Failed to create test directory: %s", err)
+       }
+
+       // Create a file before watching directory
+       const testFileAlreadyExists string = "_test/TestFsnotifyEventsExisting.testfile"
+       {
+               var f *os.File
+               f, err = os.OpenFile(testFileAlreadyExists, os.O_WRONLY|os.O_CREATE, 0666)
+               if err != nil {
+                       t.Fatalf("creating test file failed: %s", err)
+               }
+               f.Sync()
+               f.Close()
+       }
+
+       // Add a watch for testDir
+       err = watcher.Watch(testDir)
+       if err != nil {
+               t.Fatalf("Watcher.Watch() failed: %s", err)
+       }
+
+       // Add a watch for testFile
+       err = watcher.Watch(testFileAlreadyExists)
+       if err != nil {
+               t.Fatalf("Watcher.Watch() failed: %s", err)
+       }
+
+       // Receive errors on the error channel on a separate goroutine
+       go func() {
+               for err := range watcher.Error {
+                       t.Fatalf("error received: %s", err)
+               }
+       }()
+
+       // Receive events on the event channel on a separate goroutine
+       eventstream := watcher.Event
+       var deleteReceived = 0
+       done := make(chan bool)
+       go func() {
+               for event := range eventstream {
+                       // Only count relevant events
+                       if event.Name == testDir || event.Name == testFileAlreadyExists {
+                               t.Logf("event received: %s", event)
+                               if event.IsDelete() {
+                                       deleteReceived++
+                               }
+                       } else {
+                               t.Logf("unexpected event received: %s", event)
+                       }
+               }
+               done <- true
+       }()
+
+       os.RemoveAll(testDir)
+
+       // We expect this event to be received almost immediately, but let's wait 500 ms to be sure
+       time.Sleep(500 * time.Millisecond)
+       if deleteReceived != 2 {
+               t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", deleteReceived, 2)
+       }
+
+       // Try closing the fsnotify instance
+       t.Log("calling Close()")
+       watcher.Close()
+       t.Log("waiting for the event channel to become closed...")
+       select {
+       case <-done:
+               t.Log("event channel closed")
+       case <-time.After(2 * time.Second):
+               t.Fatal("event stream was not closed after 2 seconds")
+       }
+}
+
 func TestFsnotifySubDir(t *testing.T) {
        // Create an fsnotify watcher instance and initialize it
        watcher, err := NewWatcher()