From b81a2b5ff4158a403e8d8afa5b0e8b90edb176bc Mon Sep 17 00:00:00 2001 From: Volker Schukai <volker.schukai@schukai.com> Date: Sun, 10 Sep 2023 18:48:41 +0200 Subject: [PATCH] feat: new function StopWatching --- error.go | 21 ++++++++++++++++++++ lighthouse.go | 34 ++++++++++++++++++++++++++++---- lighthouse_test.go | 2 +- watching.go | 49 ++++++++++++++++++++++++++++++++++++++++++++-- watching_test.go | 28 ++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 7 deletions(-) diff --git a/error.go b/error.go index 47ef9f1..c5cea17 100644 --- a/error.go +++ b/error.go @@ -29,9 +29,30 @@ func (e WatcherNotActiveError) Error() string { return "Watcher is not active" } +// WatcherNotActiveError represents an error when trying to add a path that is already being watched. +type WatcherNotRunningError struct{} + +func (e WatcherNotRunningError) Error() string { + return "Watcher is not active" +} + +// WatcherAlreadyRunningError represents an error when trying to add a path that is already being watched. +type WatcherAlreadyRunningError struct{} + +func (e WatcherAlreadyRunningError) Error() string { + return "Watcher is already running" +} + // LighthouseNotActiveError represents an error when trying to add a path that is already being watched. type LighthouseNotActiveError struct{} func (e LighthouseNotActiveError) Error() string { return "lighthouse is not active" } + +// WatcherNotActiveError represents an error when trying to add a path that is already being watched. +type WatcherAlreadyActiveError struct{} + +func (e WatcherAlreadyActiveError) Error() string { + return "Watcher is already active" +} diff --git a/lighthouse.go b/lighthouse.go index 3befc5d..bbbe346 100644 --- a/lighthouse.go +++ b/lighthouse.go @@ -23,8 +23,10 @@ type lighthouse struct { fsnotify *fsnotify.Watcher mutex sync.Mutex active bool + running bool onError EventErrorCallback debounce time.Duration + quit chan struct{} } // SetOnError Methode, um onError zu setzen @@ -47,7 +49,11 @@ type Lighthouse interface { IsActiveWatched(path string) bool IsWatched(path string) bool IsRunning() bool - StartWatching() + + IsActive() bool + StartWatching() error + + StopWatching() error SetOnError(callback EventErrorCallback) SetDebounce(duration time.Duration) @@ -56,6 +62,7 @@ type Lighthouse interface { // NewLighthouse creates a new lighthouse instance. func NewLighthouse() Lighthouse { l := &lighthouse{} + l.debounce = 500 * time.Millisecond l.checkAndInit() return l @@ -63,30 +70,49 @@ func NewLighthouse() Lighthouse { func (l *lighthouse) checkAndInit() { + var err error + if l.active { return } - l.debounce = 500 * time.Millisecond - if l.watchers == nil { l.watchers = make(map[string]*Watch) } if l.fsnotify == nil { - var err error l.fsnotify, err = fsnotify.NewWatcher() if err != nil { return } } + // test if fsnotify is running + err = l.fsnotify.Add("/undefined-path-123456789") + if err != nil { + if err.Error() == "inotify instance already closed" { + l.fsnotify, err = fsnotify.NewWatcher() + if err != nil { + return + } + } + } + l.active = true } // IsRunning returns true if the watcher is active, false otherwise. func (l *lighthouse) IsRunning() bool { + l.mutex.Lock() + defer l.mutex.Unlock() + return l.running +} + +// IsActive returns true if the watcher is active, false otherwise. +func (l *lighthouse) IsActive() bool { + l.mutex.Lock() + defer l.mutex.Unlock() return l.active } diff --git a/lighthouse_test.go b/lighthouse_test.go index 6cd75cc..218ea09 100644 --- a/lighthouse_test.go +++ b/lighthouse_test.go @@ -48,7 +48,7 @@ func TestRemoveWatch(t *testing.T) { func TestNewLighthouse(t *testing.T) { l := NewLighthouse() assert.NotNil(t, l) - assert.True(t, l.IsRunning()) + assert.True(t, l.IsActive()) } func TestIsWatched(t *testing.T) { diff --git a/watching.go b/watching.go index 2c9f6cd..d896c54 100644 --- a/watching.go +++ b/watching.go @@ -7,7 +7,34 @@ import ( "time" ) -func (l *lighthouse) StartWatching() { +// StopWatching stops watching the given file for changes +// If the watcher is not running, an error WatcherNotRunningError is returned. +func (l *lighthouse) StopWatching() error { + + if !l.IsRunning() { + return WatcherNotRunningError{} + } + + l.mutex.Lock() + defer l.mutex.Unlock() + + close(l.quit) + l.running = false + return nil +} + +// StartWatching starts watching the given file for changes +func (l *lighthouse) StartWatching() error { + if l.IsRunning() { + return WatcherAlreadyRunningError{} + } + + l.mutex.Lock() + defer l.mutex.Unlock() + l.quit = make(chan struct{}) + l.running = true + l.checkAndInit() + go func() { eventChannel := make(chan fsnotify.Event, 100) errorChannel := make(chan error, 100) @@ -17,7 +44,14 @@ func (l *lighthouse) StartWatching() { go func() { for { + + l.mutex.Lock() + quit := l.quit + l.mutex.Unlock() + select { + case <-quit: + return case event := <-l.fsnotify.Events: eventChannel <- event case err := <-l.fsnotify.Errors: @@ -27,7 +61,14 @@ func (l *lighthouse) StartWatching() { }() for { + + l.mutex.Lock() + quit := l.quit + l.mutex.Unlock() + select { + case <-quit: + return case event := <-eventChannel: debounceMutex.Lock() @@ -123,8 +164,12 @@ func (l *lighthouse) StartWatching() { debounceMutex.Unlock() case err := <-errorChannel: - go l.onError(err) + if l.onError != nil { + go l.onError(err) + } } } }() + + return nil } diff --git a/watching_test.go b/watching_test.go index 3d8a33e..8e176ea 100644 --- a/watching_test.go +++ b/watching_test.go @@ -143,3 +143,31 @@ func TestRecreateEvent(t *testing.T) { t.Fail() } } + +func TestStopWatching(t *testing.T) { + l := NewLighthouse() // Erstellen Sie eine neue Instanz der `lighthouse`-Struktur. + + // Start Watching + _ = l.StartWatching() + + // Erlauben Sie dem System etwas Zeit, um den Watch-Prozess zu starten. + time.Sleep(time.Second * 1) + if !l.IsRunning() { + t.Fatal("l.active is false, but should be true after StartWatching") + } + + // Stop Watching + err := l.StopWatching() + if err != nil { + t.Fatalf("StopWatching failed: %v", err) + } + + // Überprüfen Sie, ob die Goroutine und der Watcher gestoppt wurden. + if l.IsRunning() { + t.Fatal("l.active is true, but should be false after StopWatching") + } + + _ = l.StartWatching() + assert.True(t, l.IsRunning()) + +} -- GitLab