From de179ddfafbff1a2e4f249b43b08c09ff1c67aab Mon Sep 17 00:00:00 2001 From: fabiankramm Date: Mon, 19 Jul 2021 14:39:28 +0200 Subject: [PATCH] fix: prevent recursive unwatch if watchpoints left --- notify_test.go | 50 +++++++++++++++++++++++++++++++++++++++++++++++ tree_recursive.go | 13 +++++++----- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/notify_test.go b/notify_test.go index 330f75f..a4cd39d 100644 --- a/notify_test.go +++ b/notify_test.go @@ -147,6 +147,56 @@ func TestRenameInRoot(t *testing.T) { } } +func mustWatch(t *testing.T, path string) chan EventInfo { + c := make(chan EventInfo, 1) + err := Watch(path+"...", c, All) + if err != nil { + t.Fatal(err) + } + + return c +} + +func TestStopChild(t *testing.T) { + tmpDir, err := ioutil.TempDir("", "notify_test-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(tmpDir) + + // create test dir + err = os.MkdirAll(filepath.Join(tmpDir, "a/b/c"), 0755) + if err != nil { + t.Fatal(err) + } + + // watch a child and parent path across multiple channels. + // this can happen in any order. + ch1 := mustWatch(t, filepath.Join(tmpDir, "a")) + ch2 := mustWatch(t, filepath.Join(tmpDir, "a/b/c")) + + // this leads to tmpDir/a being unwatched + Stop(ch2) + + // fire an event that will never show up because the watchpoint for ./a is removed + // as well. + filePath := filepath.Join(tmpDir, "a/b/c/d") + go func() { _ = ioutil.WriteFile(filePath, []byte("X"), 0664) }() + + timeout := time.After(5 * time.Second) + for { + select { + case ev := <-ch1: + t.Log(filePath, ev.Path(), ev.Event()) + if ev.Path() == filePath && ev.Event() == Write { + return + } + case <-timeout: + t.Fatal("timed out before receiving event") + } + } +} + func TestRecreated(t *testing.T) { tmpDir, err := ioutil.TempDir("", "notify_test-") if err != nil { diff --git a/tree_recursive.go b/tree_recursive.go index 7f00dfe..177ab05 100644 --- a/tree_recursive.go +++ b/tree_recursive.go @@ -306,6 +306,7 @@ func (t *recursiveTree) Watch(path string, c chan<- EventInfo, events ...Event) func (t *recursiveTree) Stop(c chan<- EventInfo) { var err error fn := func(nd node) (e error) { + isRecursive := watchIsRecursive(nd) diff := watchDel(nd, c, all) switch { case diff == none && watchTotal(nd) == 0: @@ -315,13 +316,15 @@ func (t *recursiveTree) Stop(c chan<- EventInfo) { case diff == none: // Removing c from nd does not require shrinking its eventset. case diff[1] == 0: - if watchIsRecursive(nd) { - e = t.w.RecursiveUnwatch(nd.Name) - } else { - e = t.w.Unwatch(nd.Name) + if watchTotal(nd) == 0 { + if isRecursive { + e = t.w.RecursiveUnwatch(nd.Name) + } else { + e = t.w.Unwatch(nd.Name) + } } default: - if watchIsRecursive(nd) { + if isRecursive { e = t.w.RecursiveRewatch(nd.Name, nd.Name, diff[0], diff[1]) } else { e = t.w.Rewatch(nd.Name, diff[0], diff[1])