From: Chris Howey Date: Thu, 1 Nov 2012 12:12:29 +0000 (-0500) Subject: BSD - Fix for Issue #24 - Deleting watched directory X-Git-Tag: v1.7.2~395 X-Git-Url: https://go.fuhry.dev/?a=commitdiff_plain;h=b20bf8067ff2a543ede9094e35bf0c9380ce7658;p=fsnotify.git BSD - Fix for Issue #24 - Deleting watched directory 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. --- diff --git a/fsnotify_bsd.go b/fsnotify_bsd.go index 0dc7bad..fd66c5b 100644 --- a/fsnotify_bsd.go +++ b/fsnotify_bsd.go @@ -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 diff --git a/fsnotify_test.go b/fsnotify_test.go index 929b523..a8ee9a6 100644 --- a/fsnotify_test.go +++ b/fsnotify_test.go @@ -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()