mirror of
https://github.com/docker/compose.git
synced 2025-07-21 20:54:32 +02:00
watch: tfw you have a test that asserts broken file-watch behavior 😢 (#1354)
This commit is contained in:
parent
0482f9276a
commit
6defe7cac6
@ -5,7 +5,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -128,6 +127,26 @@ func TestWatchNonExistentPath(t *testing.T) {
|
|||||||
f.assertEvents(path)
|
f.assertEvents(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWatchNonExistentPathDoesNotFireSiblingEvent(t *testing.T) {
|
||||||
|
f := newNotifyFixture(t)
|
||||||
|
defer f.tearDown()
|
||||||
|
|
||||||
|
root := f.TempDir("root")
|
||||||
|
watchedFile := filepath.Join(root, "a.txt")
|
||||||
|
unwatchedSibling := filepath.Join(root, "b.txt")
|
||||||
|
|
||||||
|
err := f.notify.Add(watchedFile)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.fsync()
|
||||||
|
|
||||||
|
d1 := "hello\ngo\n"
|
||||||
|
f.WriteFile(unwatchedSibling, d1)
|
||||||
|
f.assertEvents()
|
||||||
|
}
|
||||||
|
|
||||||
func TestRemove(t *testing.T) {
|
func TestRemove(t *testing.T) {
|
||||||
f := newNotifyFixture(t)
|
f := newNotifyFixture(t)
|
||||||
defer f.tearDown()
|
defer f.tearDown()
|
||||||
@ -319,18 +338,13 @@ func TestWatchNonexistentDirectory(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
parent := f.JoinPath("root", "parent")
|
|
||||||
file := f.JoinPath("root", "parent", "a")
|
file := f.JoinPath("root", "parent", "a")
|
||||||
|
|
||||||
f.watch(file)
|
f.watch(file)
|
||||||
f.fsync()
|
f.fsync()
|
||||||
f.events = nil
|
f.events = nil
|
||||||
f.WriteFile(file, "hello")
|
f.WriteFile(file, "hello")
|
||||||
if runtime.GOOS == "darwin" {
|
f.assertEvents(file)
|
||||||
f.assertEvents(file)
|
|
||||||
} else {
|
|
||||||
f.assertEvents(parent, file)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type notifyFixture struct {
|
type notifyFixture struct {
|
||||||
|
@ -21,7 +21,11 @@ type naiveNotify struct {
|
|||||||
events chan fsnotify.Event
|
events chan fsnotify.Event
|
||||||
wrappedEvents chan FileEvent
|
wrappedEvents chan FileEvent
|
||||||
errors chan error
|
errors chan error
|
||||||
watchList map[string]bool
|
|
||||||
|
// Paths that we're watching that should be passed up to the caller.
|
||||||
|
// Note that we may have to watch ancestors of these paths
|
||||||
|
// in order to fulfill the API promise.
|
||||||
|
notifyList map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *naiveNotify) Add(name string) error {
|
func (d *naiveNotify) Add(name string) error {
|
||||||
@ -32,24 +36,22 @@ func (d *naiveNotify) Add(name string) error {
|
|||||||
|
|
||||||
// if it's a file that doesn't exist, watch its parent
|
// if it's a file that doesn't exist, watch its parent
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
err, fileWatched := d.watchUpRecursively(name)
|
err = d.watchAncestorOfMissingPath(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "watchUpRecursively(%q)", name)
|
return errors.Wrapf(err, "watchAncestorOfMissingPath(%q)", name)
|
||||||
}
|
}
|
||||||
d.watchList[fileWatched] = true
|
|
||||||
} else if fi.IsDir() {
|
} else if fi.IsDir() {
|
||||||
err = d.watchRecursively(name)
|
err = d.watchRecursively(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "notify.Add(%q)", name)
|
return errors.Wrapf(err, "notify.Add(%q)", name)
|
||||||
}
|
}
|
||||||
d.watchList[name] = true
|
|
||||||
} else {
|
} else {
|
||||||
err = d.watcher.Add(name)
|
err = d.watcher.Add(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "notify.Add(%q)", name)
|
return errors.Wrapf(err, "notify.Add(%q)", name)
|
||||||
}
|
}
|
||||||
d.watchList[name] = true
|
|
||||||
}
|
}
|
||||||
|
d.notifyList[name] = true
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -71,22 +73,22 @@ func (d *naiveNotify) watchRecursively(dir string) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *naiveNotify) watchUpRecursively(path string) (error, string) {
|
func (d *naiveNotify) watchAncestorOfMissingPath(path string) error {
|
||||||
if path == string(filepath.Separator) {
|
if path == string(filepath.Separator) {
|
||||||
return fmt.Errorf("cannot watch root directory"), ""
|
return fmt.Errorf("cannot watch root directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := os.Stat(path)
|
_, err := os.Stat(path)
|
||||||
if err != nil && !os.IsNotExist(err) {
|
if err != nil && !os.IsNotExist(err) {
|
||||||
return errors.Wrapf(err, "os.Stat(%q)", path), ""
|
return errors.Wrapf(err, "os.Stat(%q)", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
parent := filepath.Dir(path)
|
parent := filepath.Dir(path)
|
||||||
return d.watchUpRecursively(parent)
|
return d.watchAncestorOfMissingPath(parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.watcher.Add(path), path
|
return d.watcher.Add(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *naiveNotify) Close() error {
|
func (d *naiveNotify) Close() error {
|
||||||
@ -122,35 +124,39 @@ func (d *naiveNotify) loop() {
|
|||||||
Op: fsnotify.Create,
|
Op: fsnotify.Create,
|
||||||
Name: path,
|
Name: path,
|
||||||
}
|
}
|
||||||
d.sendEventIfWatched(newE)
|
|
||||||
// TODO(dmiller): symlinks 😭
|
if d.shouldNotify(newE) {
|
||||||
err = d.Add(path)
|
d.wrappedEvents <- FileEvent{newE.Name}
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error watching path %s: %s", e.Name, err)
|
// TODO(dmiller): symlinks 😭
|
||||||
|
err = d.Add(path)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error watching path %s: %s", e.Name, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error walking directory %s: %s", e.Name, err)
|
log.Printf("Error walking directory %s: %s", e.Name, err)
|
||||||
}
|
}
|
||||||
} else {
|
} else if d.shouldNotify(e) {
|
||||||
d.sendEventIfWatched(e)
|
d.wrappedEvents <- FileEvent{e.Name}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *naiveNotify) sendEventIfWatched(e fsnotify.Event) {
|
func (d *naiveNotify) shouldNotify(e fsnotify.Event) bool {
|
||||||
if _, ok := d.watchList[e.Name]; ok {
|
if _, ok := d.notifyList[e.Name]; ok {
|
||||||
d.wrappedEvents <- FileEvent{e.Name}
|
return true
|
||||||
} else {
|
} else {
|
||||||
// TODO(dmiller): maybe use a prefix tree here?
|
// TODO(dmiller): maybe use a prefix tree here?
|
||||||
for path := range d.watchList {
|
for path := range d.notifyList {
|
||||||
if pathIsChildOf(e.Name, path) {
|
if pathIsChildOf(e.Name, path) {
|
||||||
d.wrappedEvents <- FileEvent{e.Name}
|
return true
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWatcher() (*naiveNotify, error) {
|
func NewWatcher() (*naiveNotify, error) {
|
||||||
@ -166,7 +172,7 @@ func NewWatcher() (*naiveNotify, error) {
|
|||||||
events: fsw.Events,
|
events: fsw.Events,
|
||||||
wrappedEvents: wrappedEvents,
|
wrappedEvents: wrappedEvents,
|
||||||
errors: fsw.Errors,
|
errors: fsw.Errors,
|
||||||
watchList: map[string]bool{},
|
notifyList: map[string]bool{},
|
||||||
}
|
}
|
||||||
|
|
||||||
go wmw.loop()
|
go wmw.loop()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user