From be92367a8fe3ebe30e355b978b7ecc7359476bac Mon Sep 17 00:00:00 2001 From: Chris Howey Date: Tue, 25 Sep 2012 19:57:52 -0500 Subject: [PATCH] BSD - fix to not longer get duplicate CREATE events needed to add DELETE watch for directories that are created in watched directory Also added a test for this behaviour --- fsnotify_bsd.go | 20 +++++++++++++++++--- fsnotify_test.go | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/fsnotify_bsd.go b/fsnotify_bsd.go index 5ddf578..015648b 100644 --- a/fsnotify_bsd.go +++ b/fsnotify_bsd.go @@ -42,6 +42,7 @@ type Watcher struct { fsnFlags map[string]uint32 // Map of watched files to flags used for filter paths map[int]string // Map of watched paths (key: watch descriptor) finfo map[int]os.FileInfo // Map of file information (isDir, isReg; key: watch descriptor) + fileExists map[string]bool // Keep track of if we know this file exists (to stop duplicate create events) Error chan error // Errors are sent on this channel internalEvent chan *FileEvent // Events are queued on this channel Event chan *FileEvent // Events are returned on this channel @@ -62,6 +63,7 @@ func NewWatcher() (*Watcher, error) { fsnFlags: make(map[string]uint32), paths: make(map[int]string), finfo: make(map[int]os.FileInfo), + fileExists: make(map[string]bool), internalEvent: make(chan *FileEvent), Event: make(chan *FileEvent), Error: make(chan error), @@ -249,6 +251,10 @@ func (w *Watcher) readEvents() { if fileEvent.IsRename() { w.removeWatch(fileEvent.Name) + delete(w.fileExists, fileEvent.Name) + } + if fileEvent.IsDelete() { + delete(w.fileExists, fileEvent.Name) } } } @@ -263,15 +269,22 @@ func (w *Watcher) watchDirectoryFiles(dirPath string) error { // Search for new files for _, fileInfo := range files { + filePath := filepath.Join(dirPath, fileInfo.Name()) if fileInfo.IsDir() == false { - filePath := filepath.Join(dirPath, fileInfo.Name()) // Watch file to mimic linux fsnotify e := w.addWatch(filePath, NOTE_DELETE|NOTE_WRITE|NOTE_RENAME) w.fsnFlags[filePath] = FSN_ALL if e != nil { return e } + } else { + // Linux gives deletes if not explicitly watching + e := w.addWatch(filePath, NOTE_DELETE) + if e != nil { + return e + } } + w.fileExists[filePath] = true } return nil @@ -291,8 +304,8 @@ func (w *Watcher) sendDirectoryChangeEvents(dirPath string) { // Search for new files for _, fileInfo := range files { filePath := filepath.Join(dirPath, fileInfo.Name()) - _, watchFound := w.watches[filePath] - if watchFound == false { + _, doesExist := w.fileExists[filePath] + if doesExist == false { w.fsnFlags[filePath] = FSN_ALL // Send create event fileEvent := new(FileEvent) @@ -300,6 +313,7 @@ func (w *Watcher) sendDirectoryChangeEvents(dirPath string) { fileEvent.create = true w.internalEvent <- fileEvent } + w.fileExists[filePath] = true } w.watchDirectoryFiles(dirPath) } diff --git a/fsnotify_test.go b/fsnotify_test.go index f586165..1df2fdb 100644 --- a/fsnotify_test.go +++ b/fsnotify_test.go @@ -265,7 +265,9 @@ func TestFsnotifySubDir(t *testing.T) { } const testDir string = "_test" + const testFile1 string = "_test/TestFsnotifyFile1.testfile" const testSubDir string = "_test/sub" + const testSubDirFile string = "_test/sub/TestFsnotifyFile1.testfile" // Create directory to watch if os.Mkdir(testDir, 0777) != nil { @@ -283,15 +285,19 @@ func TestFsnotifySubDir(t *testing.T) { // Receive events on the event channel on a separate goroutine eventstream := watcher.Event var createReceived = 0 + var deleteReceived = 0 done := make(chan bool) go func() { for event := range eventstream { // Only count relevant events - if event.Name == testDir || event.Name == testSubDir { + if event.Name == testDir || event.Name == testSubDir || event.Name == testFile1 { t.Logf("event received: %s", event) if event.IsCreate() { createReceived++ } + if event.IsDelete() { + deleteReceived++ + } } else { t.Logf("unexpected event received: %s", event) } @@ -310,10 +316,35 @@ func TestFsnotifySubDir(t *testing.T) { t.Fatalf("Failed to create test sub-directory: %s", err) } + // Create a file + var f *os.File + f, err = os.OpenFile(testFile1, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + f.Sync() + f.Close() + + // Create a file (Should not see this! we are not watching subdir) + var fs *os.File + fs, err = os.OpenFile(testSubDirFile, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + t.Fatalf("creating test file failed: %s", err) + } + fs.Sync() + fs.Close() + + // Make sure receive deletes for both file and sub-directory + os.RemoveAll(testSubDir) + os.Remove(testFile1) + // We expect this event to be received almost immediately, but let's wait 500 ms to be sure time.Sleep(500 * time.Millisecond) - if createReceived != 1 { - t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", createReceived, 1) + if createReceived != 2 { + t.Fatalf("incorrect number of create events received after 500 ms (%d vs %d)", createReceived, 2) + } + if deleteReceived != 2 { + t.Fatalf("incorrect number of delete events received after 500 ms (%d vs %d)", deleteReceived, 2) } // Try closing the fsnotify instance -- 2.50.1