diff --git a/notify_test.go b/notify_test.go index a4cd39d..c607d65 100644 --- a/notify_test.go +++ b/notify_test.go @@ -147,6 +147,27 @@ func TestRenameInRoot(t *testing.T) { } } +func prepareTestDir(t *testing.T) string { + tmpDir, err := ioutil.TempDir("", "notify_test-") + if err != nil { + t.Fatal(err) + } + + // resolve paths on OSX + s, err := filepath.EvalSymlinks(tmpDir) + if err != nil { + t.Fatal(err) + } + + // create test dir + err = os.MkdirAll(filepath.Join(s, "a/b/c"), 0755) + if err != nil { + t.Fatal(err) + } + + return s +} + func mustWatch(t *testing.T, path string) chan EventInfo { c := make(chan EventInfo, 1) err := Watch(path+"...", c, All) @@ -157,18 +178,43 @@ func mustWatch(t *testing.T, path string) chan EventInfo { return c } -func TestStopChild(t *testing.T) { - tmpDir, err := ioutil.TempDir("", "notify_test-") - if err != nil { - t.Fatal(err) - } +func TestAddParentAfterStop(t *testing.T) { + tmpDir := prepareTestDir(t) 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/b")) + ch2 := mustWatch(t, filepath.Join(tmpDir, "a/b/c")) + + // unwatch ./a/b -- this is what causes the panic on the next line. + // note that this also fails if we notify.Stop(ch1) instead. + Stop(ch1) + + // add parent watchpoint + _ = mustWatch(t, filepath.Join(tmpDir, "a")) + + // fire an event + 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 := <-ch2: + t.Log(ev.Path(), ev.Event()) + if ev.Path() == filePath && ev.Event() == Write { + return + } + case <-timeout: + t.Fatal("timed out before receiving event") + } } +} + +func TestStopChild(t *testing.T) { + tmpDir := prepareTestDir(t) + defer os.RemoveAll(tmpDir) // watch a child and parent path across multiple channels. // this can happen in any order. @@ -178,8 +224,7 @@ func TestStopChild(t *testing.T) { // 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. + // fire an event filePath := filepath.Join(tmpDir, "a/b/c/d") go func() { _ = ioutil.WriteFile(filePath, []byte("X"), 0664) }() @@ -187,7 +232,7 @@ func TestStopChild(t *testing.T) { for { select { case ev := <-ch1: - t.Log(filePath, ev.Path(), ev.Event()) + t.Log(ev.Path(), ev.Event()) if ev.Path() == filePath && ev.Event() == Write { return } diff --git a/tree_recursive.go b/tree_recursive.go index 177ab05..c7382c7 100644 --- a/tree_recursive.go +++ b/tree_recursive.go @@ -47,6 +47,10 @@ func watchCopy(src, dst node) { } if wpsrc := src.Child[""].Watch; len(wpsrc) != 0 { wpdst := dst.Child[""].Watch + if wpdst == nil { + wpdst = make(watchpoint) + dst.Child[""] = node{Watch: wpdst} + } for c, e := range wpsrc { if c == nil { continue @@ -226,7 +230,7 @@ func (t *recursiveTree) Watch(path string, c chan<- EventInfo, events ...Event) // Look for children nodes, unwatch n-1 of them and rewatch the last one. var children []node fn := func(nd node) error { - if len(nd.Watch) == 0 { + if watchTotal(nd) == 0 { return nil } children = append(children, nd)