diff --git a/pkg/watch/notify.go b/pkg/watch/notify.go index 02f7d735d..a0e170dfa 100644 --- a/pkg/watch/notify.go +++ b/pkg/watch/notify.go @@ -1,6 +1,14 @@ package watch -import "github.com/windmilleng/tilt/internal/logger" +import ( + "expvar" + + "github.com/windmilleng/tilt/internal/logger" +) + +var ( + numberOfWatches = expvar.NewInt("watch.naive.numberOfWatches") +) type FileEvent struct { Path string diff --git a/pkg/watch/notify_test.go b/pkg/watch/notify_test.go index 83086b631..b172accec 100644 --- a/pkg/watch/notify_test.go +++ b/pkg/watch/notify_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/assert" + "github.com/windmilleng/tilt/internal/dockerignore" "github.com/windmilleng/tilt/internal/logger" "github.com/windmilleng/tilt/internal/testutils/tempdir" ) @@ -412,36 +413,99 @@ func TestWatchNonexistentDirectory(t *testing.T) { } } -// doesn't work on linux -// func TestWatchNonexistentFileInNonexistentDirectory(t *testing.T) { -// f := newNotifyFixture(t) -// defer f.tearDown() +func TestWatchNonexistentFileInNonexistentDirectory(t *testing.T) { + f := newNotifyFixture(t) + defer f.tearDown() -// root := f.JoinPath("root") -// err := os.Mkdir(root, 0777) -// if err != nil { -// t.Fatal(err) -// } -// parent := f.JoinPath("parent") -// file := f.JoinPath("parent", "a") + root := f.JoinPath("root") + err := os.Mkdir(root, 0777) + if err != nil { + t.Fatal(err) + } + parent := f.JoinPath("parent") + file := f.JoinPath("parent", "a") -// f.watch(file) -// f.assertEvents() + f.watch(file) + f.assertEvents() -// err = os.Mkdir(parent, 0777) -// if err != nil { -// t.Fatal(err) -// } + err = os.Mkdir(parent, 0777) + if err != nil { + t.Fatal(err) + } -// f.assertEvents() -// f.WriteFile(file, "hello") -// f.assertEvents(file) -// } + f.assertEvents() + f.WriteFile(file, "hello") + f.assertEvents(file) +} + +func TestWatchCountInnerFile(t *testing.T) { + f := newNotifyFixture(t) + defer f.tearDown() + + root := f.paths[0] + a := f.JoinPath(root, "a") + b := f.JoinPath(a, "b") + file := f.JoinPath(b, "bigFile") + f.WriteFile(file, "hello") + f.assertEvents(a, b, file) + + expectedWatches := 3 + if runtime.GOOS == "darwin" { + expectedWatches = 1 + } + assert.Equal(t, expectedWatches, int(numberOfWatches.Value())) +} + +func TestWatchCountInnerFileWithIgnore(t *testing.T) { + f := newNotifyFixture(t) + defer f.tearDown() + + root := f.paths[0] + ignore, _ := dockerignore.NewDockerPatternMatcher(root, []string{ + "a", + "!a/b", + }) + f.setIgnore(ignore) + + a := f.JoinPath(root, "a") + b := f.JoinPath(a, "b") + file := f.JoinPath(b, "bigFile") + f.WriteFile(file, "hello") + f.assertEvents(b, file) + + expectedWatches := 3 + if runtime.GOOS == "darwin" { + expectedWatches = 1 + } + assert.Equal(t, expectedWatches, int(numberOfWatches.Value())) +} + +func TestIgnore(t *testing.T) { + f := newNotifyFixture(t) + defer f.tearDown() + + root := f.paths[0] + ignore, _ := dockerignore.NewDockerPatternMatcher(root, []string{"a/b"}) + f.setIgnore(ignore) + + a := f.JoinPath(root, "a") + b := f.JoinPath(a, "b") + file := f.JoinPath(b, "bigFile") + f.WriteFile(file, "hello") + f.assertEvents(a) + + expectedWatches := 3 + if runtime.GOOS == "darwin" { + expectedWatches = 1 + } + assert.Equal(t, expectedWatches, int(numberOfWatches.Value())) +} type notifyFixture struct { out *bytes.Buffer *tempdir.TempDirFixture notify Notify + ignore PathMatcher paths []string events []FileEvent } @@ -451,15 +515,24 @@ func newNotifyFixture(t *testing.T) *notifyFixture { nf := ¬ifyFixture{ TempDirFixture: tempdir.NewTempDirFixture(t), paths: []string{}, + ignore: EmptyMatcher{}, out: out, } nf.watch(nf.TempDir("watched")) return nf } +func (f *notifyFixture) setIgnore(ignore PathMatcher) { + f.ignore = ignore + f.rebuildWatcher() +} + func (f *notifyFixture) watch(path string) { f.paths = append(f.paths, path) + f.rebuildWatcher() +} +func (f *notifyFixture) rebuildWatcher() { // sync any outstanding events and close the old watcher if f.notify != nil { f.fsync() @@ -467,7 +540,7 @@ func (f *notifyFixture) watch(path string) { } // create a new watcher - notify, err := NewWatcher(f.paths, EmptyMatcher{}, logger.NewLogger(logger.DebugLvl, f.out)) + notify, err := NewWatcher(f.paths, f.ignore, logger.NewLogger(logger.DebugLvl, f.out)) if err != nil { f.T().Fatal(err) } @@ -569,4 +642,5 @@ func (f *notifyFixture) closeWatcher() { func (f *notifyFixture) tearDown() { f.closeWatcher() f.TempDirFixture.TearDown() + numberOfWatches.Set(0) } diff --git a/pkg/watch/watcher_darwin.go b/pkg/watch/watcher_darwin.go index 038f940fd..17e476e21 100644 --- a/pkg/watch/watcher_darwin.go +++ b/pkg/watch/watcher_darwin.go @@ -89,6 +89,8 @@ func (d *darwinNotify) initAdd(name string) { } func (d *darwinNotify) Start() error { + numberOfWatches.Add(int64(len(d.stream.Paths))) + d.stream.Start() go d.loop() @@ -97,6 +99,8 @@ func (d *darwinNotify) Start() error { } func (d *darwinNotify) Close() error { + numberOfWatches.Add(int64(-len(d.stream.Paths))) + d.stream.Stop() close(d.errors) close(d.stop) diff --git a/pkg/watch/watcher_naive.go b/pkg/watch/watcher_naive.go index 4ddd58b17..8fd2975f9 100644 --- a/pkg/watch/watcher_naive.go +++ b/pkg/watch/watcher_naive.go @@ -3,7 +3,6 @@ package watch import ( - "expvar" "fmt" "os" "path/filepath" @@ -32,12 +31,9 @@ type naiveNotify struct { events chan fsnotify.Event wrappedEvents chan FileEvent errors chan error + numWatches int64 } -var ( - numberOfWatches = expvar.NewInt("watch.naive.numberOfWatches") -) - func (d *naiveNotify) Start() error { for name := range d.notifyList { fi, err := os.Stat(name) @@ -108,6 +104,8 @@ func (d *naiveNotify) watchAncestorOfMissingPath(path string) error { } func (d *naiveNotify) Close() error { + numberOfWatches.Add(-d.numWatches) + d.numWatches = 0 return d.watcher.Close() } @@ -191,6 +189,7 @@ func (d *naiveNotify) add(path string) error { if err != nil { return err } + d.numWatches++ numberOfWatches.Add(1) return nil }