Skip to content

Commit 5e0f1ee

Browse files
nicksndeloof
authored andcommitted
watch: fix spurious errors while watching (docker#1726)
1 parent f82e2de commit 5e0f1ee

File tree

3 files changed

+98
-9
lines changed

3 files changed

+98
-9
lines changed

pkg/watch/notify_test.go

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package watch
22

33
import (
4+
"bytes"
5+
"context"
46
"fmt"
57
"io/ioutil"
68
"os"
@@ -10,6 +12,8 @@ import (
1012
"testing"
1113
"time"
1214

15+
"github.com/stretchr/testify/assert"
16+
"github.com/windmilleng/tilt/internal/logger"
1317
"github.com/windmilleng/tilt/internal/testutils/tempdir"
1418
)
1519

@@ -54,6 +58,65 @@ func TestEventOrdering(t *testing.T) {
5458
f.assertEvents(expected...)
5559
}
5660

61+
// Simulate a git branch switch that creates a bunch
62+
// of directories, creates files in them, then deletes
63+
// them all quickly. Make sure there are no errors.
64+
func TestGitBranchSwitch(t *testing.T) {
65+
f := newNotifyFixture(t)
66+
defer f.tearDown()
67+
68+
count := 10
69+
dirs := make([]string, count)
70+
for i, _ := range dirs {
71+
dir := f.TempDir("watched")
72+
dirs[i] = dir
73+
err := f.notify.Add(dir)
74+
if err != nil {
75+
t.Fatal(err)
76+
}
77+
}
78+
79+
f.fsync()
80+
f.events = nil
81+
82+
// consume all the events in the background
83+
ctx, cancel := context.WithCancel(context.Background())
84+
done := f.consumeEventsInBackground(ctx)
85+
86+
for i, dir := range dirs {
87+
for j := 0; j < count; j++ {
88+
base := fmt.Sprintf("x/y/dir-%d/x.txt", j)
89+
p := filepath.Join(dir, base)
90+
f.WriteFile(p, "contents")
91+
}
92+
93+
if i != 0 {
94+
os.RemoveAll(dir)
95+
}
96+
}
97+
98+
cancel()
99+
err := <-done
100+
if err != nil {
101+
t.Fatal(err)
102+
}
103+
104+
f.fsync()
105+
f.events = nil
106+
107+
// Make sure the watch on the first dir still works.
108+
dir := dirs[0]
109+
path := filepath.Join(dir, "change")
110+
111+
f.WriteFile(path, "hello\n")
112+
f.fsync()
113+
114+
f.assertEvents(path)
115+
116+
// Make sure there are no errors in the out stream
117+
assert.Equal(t, "", f.out.String())
118+
}
119+
57120
func TestWatchesAreRecursive(t *testing.T) {
58121
f := newNotifyFixture(t)
59122
defer f.tearDown()
@@ -412,14 +475,16 @@ func TestWatchNonexistentDirectory(t *testing.T) {
412475
// }
413476

414477
type notifyFixture struct {
478+
out *bytes.Buffer
415479
*tempdir.TempDirFixture
416480
notify Notify
417481
watched string
418482
events []FileEvent
419483
}
420484

421485
func newNotifyFixture(t *testing.T) *notifyFixture {
422-
notify, err := NewWatcher()
486+
out := bytes.NewBuffer(nil)
487+
notify, err := NewWatcher(logger.NewLogger(logger.DebugLvl, out))
423488
if err != nil {
424489
t.Fatal(err)
425490
}
@@ -435,6 +500,7 @@ func newNotifyFixture(t *testing.T) *notifyFixture {
435500
TempDirFixture: f,
436501
watched: watched,
437502
notify: notify,
503+
out: out,
438504
}
439505
}
440506

@@ -460,6 +526,25 @@ func (f *notifyFixture) assertEvents(expected ...string) {
460526
}
461527
}
462528

529+
func (f *notifyFixture) consumeEventsInBackground(ctx context.Context) chan error {
530+
done := make(chan error)
531+
go func() {
532+
for {
533+
select {
534+
case <-ctx.Done():
535+
close(done)
536+
return
537+
case err := <-f.notify.Errors():
538+
done <- err
539+
close(done)
540+
return
541+
case <-f.notify.Events():
542+
}
543+
}
544+
}()
545+
return done
546+
}
547+
463548
func (f *notifyFixture) fsync() {
464549
syncPathBase := fmt.Sprintf("sync-%d.txt", time.Now().UnixNano())
465550
syncPath := filepath.Join(f.watched, syncPathBase)

pkg/watch/watcher_darwin.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"sync"
66
"time"
77

8+
"github.com/windmilleng/tilt/internal/logger"
89
"github.com/windmilleng/tilt/internal/ospath"
910

1011
"github.com/windmilleng/fsevents"
@@ -120,7 +121,7 @@ func (d *darwinNotify) Errors() chan error {
120121
return d.errors
121122
}
122123

123-
func NewWatcher() (Notify, error) {
124+
func NewWatcher(l logger.Logger) (Notify, error) {
124125
dw := &darwinNotify{
125126
stream: &fsevents.EventStream{
126127
Latency: 1 * time.Millisecond,

pkg/watch/watcher_naive.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ package watch
44

55
import (
66
"fmt"
7-
"log"
87
"os"
98
"path/filepath"
109
"sync"
1110

1211
"github.com/pkg/errors"
1312
"github.com/windmilleng/fsnotify"
1413

14+
"github.com/windmilleng/tilt/internal/logger"
1515
"github.com/windmilleng/tilt/internal/ospath"
1616
)
1717

@@ -20,6 +20,7 @@ import (
2020
//
2121
// All OS-specific codepaths are handled by fsnotify.
2222
type naiveNotify struct {
23+
log logger.Logger
2324
watcher *fsnotify.Watcher
2425
events chan fsnotify.Event
2526
wrappedEvents chan FileEvent
@@ -127,7 +128,7 @@ func (d *naiveNotify) loop() {
127128
}
128129

129130
// TODO(dbentley): if there's a delete should we call d.watcher.Remove to prevent leaking?
130-
if err := filepath.Walk(e.Name, func(path string, mode os.FileInfo, err error) error {
131+
err := filepath.Walk(e.Name, func(path string, mode os.FileInfo, err error) error {
131132
if err != nil {
132133
return err
133134
}
@@ -151,13 +152,14 @@ func (d *naiveNotify) loop() {
151152
}
152153
if shouldWatch {
153154
err := d.watcher.Add(path)
154-
if err != nil {
155-
log.Printf("Error watching path %s: %s", e.Name, err)
155+
if err != nil && !os.IsNotExist(err) {
156+
d.log.Infof("Error watching path %s: %s", e.Name, err)
156157
}
157158
}
158159
return nil
159-
}); err != nil {
160-
log.Printf("Error walking directory %s: %s", e.Name, err)
160+
})
161+
if err != nil && !os.IsNotExist(err) {
162+
d.log.Infof("Error walking directory %s: %s", e.Name, err)
161163
}
162164
}
163165
}
@@ -177,7 +179,7 @@ func (d *naiveNotify) shouldNotify(path string) bool {
177179
return false
178180
}
179181

180-
func NewWatcher() (*naiveNotify, error) {
182+
func NewWatcher(l logger.Logger) (*naiveNotify, error) {
181183
fsw, err := fsnotify.NewWatcher()
182184
if err != nil {
183185
return nil, err
@@ -186,6 +188,7 @@ func NewWatcher() (*naiveNotify, error) {
186188
wrappedEvents := make(chan FileEvent)
187189

188190
wmw := &naiveNotify{
191+
log: l,
189192
watcher: fsw,
190193
events: fsw.Events,
191194
wrappedEvents: wrappedEvents,

0 commit comments

Comments
 (0)