| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- package watch
- import (
- "fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "runtime"
- "strings"
- "testing"
- "time"
- "github.com/windmilleng/fsnotify"
- )
- // Each implementation of the notify interface should have the same basic
- // behavior.
- func TestNoEvents(t *testing.T) {
- f := newNotifyFixture(t)
- defer f.tearDown()
- f.fsync()
- f.assertEvents()
- }
- func TestEventOrdering(t *testing.T) {
- f := newNotifyFixture(t)
- defer f.tearDown()
- count := 8
- dirs := make([]string, count)
- for i, _ := range dirs {
- dir, err := f.root.NewDir("watched")
- if err != nil {
- t.Fatal(err)
- }
- dirs[i] = dir.Path()
- err = f.notify.Add(dir.Path())
- if err != nil {
- t.Fatal(err)
- }
- }
- f.fsync()
- f.events = nil
- var expected []fsnotify.Event
- for i, dir := range dirs {
- base := fmt.Sprintf("%d.txt", i)
- p := filepath.Join(dir, base)
- err := ioutil.WriteFile(p, []byte(base), os.FileMode(0777))
- if err != nil {
- t.Fatal(err)
- }
- expected = append(expected, create(filepath.Join(dir, base)))
- }
- f.fsync()
- f.filterJustCreateEvents()
- f.assertEvents(expected...)
- // Check to make sure that the files appeared in the right order.
- createEvents := make([]fsnotify.Event, 0, count)
- for _, e := range f.events {
- if e.Op == fsnotify.Create {
- createEvents = append(createEvents, e)
- }
- }
- if len(createEvents) != count {
- t.Fatalf("Expected %d create events. Actual: %+v", count, createEvents)
- }
- for i, event := range createEvents {
- base := fmt.Sprintf("%d.txt", i)
- p := filepath.Join(dirs[i], base)
- if event.Name != p {
- t.Fatalf("Expected event %q at %d. Actual: %+v", base, i, createEvents)
- }
- }
- }
- func TestWatchesAreRecursive(t *testing.T) {
- f := newNotifyFixture(t)
- defer f.tearDown()
- root, err := f.root.NewDir("root")
- if err != nil {
- t.Fatal(err)
- }
- // add a sub directory
- subPath := filepath.Join(root.Path(), "sub")
- err = os.MkdirAll(subPath, os.ModePerm)
- if err != nil {
- t.Fatal(err)
- }
- // watch parent
- err = f.notify.Add(root.Path())
- if err != nil {
- t.Fatal(err)
- }
- f.fsync()
- f.events = nil
- // change sub directory
- changeFilePath := filepath.Join(subPath, "change")
- _, err = os.OpenFile(changeFilePath, os.O_RDONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatal(err)
- }
- // we should get notified
- f.fsync()
- f.assertEvents(create(changeFilePath))
- }
- func TestNewDirectoriesAreRecursivelyWatched(t *testing.T) {
- f := newNotifyFixture(t)
- defer f.tearDown()
- root, err := f.root.NewDir("root")
- if err != nil {
- t.Fatal(err)
- }
- // watch parent
- err = f.notify.Add(root.Path())
- if err != nil {
- t.Fatal(err)
- }
- f.fsync()
- f.events = nil
- // add a sub directory
- subPath := filepath.Join(root.Path(), "sub")
- err = os.MkdirAll(subPath, os.ModePerm)
- if err != nil {
- f.t.Fatal(err)
- }
- // change something inside sub directory
- changeFilePath := filepath.Join(subPath, "change")
- _, err = os.OpenFile(changeFilePath, os.O_RDONLY|os.O_CREATE, 0666)
- if err != nil {
- t.Fatal(err)
- }
- // we should get notified
- f.fsync()
- // assert events
- f.assertEvents(create(subPath), create(changeFilePath))
- }
- func TestWatchNonExistentPath(t *testing.T) {
- f := newNotifyFixture(t)
- defer f.tearDown()
- root, err := f.root.NewDir("root")
- if err != nil {
- t.Fatal(err)
- }
- path := filepath.Join(root.Path(), "change")
- err = f.notify.Add(path)
- if err != nil {
- t.Fatal(err)
- }
- d1 := []byte("hello\ngo\n")
- err = ioutil.WriteFile(path, d1, 0644)
- if err != nil {
- t.Fatal(err)
- }
- f.fsync()
- if runtime.GOOS == "darwin" {
- f.assertEvents(create(path))
- } else {
- f.assertEvents(create(path), write(path))
- }
- }
- func TestRemove(t *testing.T) {
- f := newNotifyFixture(t)
- defer f.tearDown()
- root, err := f.root.NewDir("root")
- if err != nil {
- t.Fatal(err)
- }
- path := filepath.Join(root.Path(), "change")
- if err != nil {
- t.Fatal(err)
- }
- d1 := []byte("hello\ngo\n")
- err = ioutil.WriteFile(path, d1, 0644)
- if err != nil {
- t.Fatal(err)
- }
- err = f.notify.Add(path)
- if err != nil {
- t.Fatal(err)
- }
- f.fsync()
- f.events = nil
- err = os.Remove(path)
- if err != nil {
- t.Fatal(err)
- }
- f.fsync()
- f.assertEvents(remove(path))
- }
- func TestRemoveAndAddBack(t *testing.T) {
- t.Skip("Skipping broken test for now")
- f := newNotifyFixture(t)
- defer f.tearDown()
- root, err := f.root.NewDir("root")
- if err != nil {
- t.Fatal(err)
- }
- path := filepath.Join(root.Path(), "change")
- if err != nil {
- t.Fatal(err)
- }
- d1 := []byte("hello\ngo\n")
- err = ioutil.WriteFile(path, d1, 0644)
- if err != nil {
- t.Fatal(err)
- }
- err = f.notify.Add(path)
- if err != nil {
- t.Fatal(err)
- }
- err = os.Remove(path)
- if err != nil {
- t.Fatal(err)
- }
- f.fsync()
- f.assertEvents(remove(path))
- f.events = nil
- err = ioutil.WriteFile(path, d1, 0644)
- if err != nil {
- t.Fatal(err)
- }
- f.assertEvents(create(path))
- }
- func TestSingleFile(t *testing.T) {
- if runtime.GOOS != "darwin" {
- t.Skip("Broken on Linux")
- }
- f := newNotifyFixture(t)
- defer f.tearDown()
- root, err := f.root.NewDir("root")
- if err != nil {
- t.Fatal(err)
- }
- path := filepath.Join(root.Path(), "change")
- if err != nil {
- t.Fatal(err)
- }
- d1 := []byte("hello\ngo\n")
- err = ioutil.WriteFile(path, d1, 0644)
- if err != nil {
- t.Fatal(err)
- }
- err = f.notify.Add(path)
- if err != nil {
- t.Fatal(err)
- }
- d2 := []byte("hello\nworld\n")
- err = ioutil.WriteFile(path, d2, 0644)
- if err != nil {
- t.Fatal(err)
- }
- f.fsync()
- f.assertEvents(create(path))
- }
- type notifyFixture struct {
- t *testing.T
- root *TempDir
- watched *TempDir
- notify Notify
- events []fsnotify.Event
- }
- func newNotifyFixture(t *testing.T) *notifyFixture {
- SetLimitChecksEnabled(false)
- notify, err := NewWatcher()
- if err != nil {
- t.Fatal(err)
- }
- root, err := NewDir(t.Name())
- if err != nil {
- t.Fatal(err)
- }
- watched, err := root.NewDir("watched")
- if err != nil {
- t.Fatal(err)
- }
- err = notify.Add(watched.Path())
- if err != nil {
- t.Fatal(err)
- }
- return ¬ifyFixture{
- t: t,
- root: root,
- watched: watched,
- notify: notify,
- }
- }
- func (f *notifyFixture) filterJustCreateEvents() {
- var r []fsnotify.Event
- for _, ev := range f.events {
- if ev.Op != fsnotify.Create {
- continue
- }
- r = append(r, ev)
- }
- f.events = r
- }
- func (f *notifyFixture) assertEvents(expected ...fsnotify.Event) {
- if len(f.events) != len(expected) {
- f.t.Fatalf("Got %d events (expected %d): %v %v", len(f.events), len(expected), f.events, expected)
- }
- for i, actual := range f.events {
- if actual != expected[i] {
- f.t.Fatalf("Got event %v (expected %v)", actual, expected[i])
- }
- }
- }
- func create(f string) fsnotify.Event {
- return fsnotify.Event{
- Name: f,
- Op: fsnotify.Create,
- }
- }
- func write(f string) fsnotify.Event {
- return fsnotify.Event{
- Name: f,
- Op: fsnotify.Write,
- }
- }
- func remove(f string) fsnotify.Event {
- return fsnotify.Event{
- Name: f,
- Op: fsnotify.Remove,
- }
- }
- func (f *notifyFixture) fsync() {
- syncPathBase := fmt.Sprintf("sync-%d.txt", time.Now().UnixNano())
- syncPath := filepath.Join(f.watched.Path(), syncPathBase)
- anySyncPath := filepath.Join(f.watched.Path(), "sync-")
- timeout := time.After(time.Second)
- err := ioutil.WriteFile(syncPath, []byte(fmt.Sprintf("%s", time.Now())), os.FileMode(0777))
- if err != nil {
- f.t.Fatal(err)
- }
- F:
- for {
- select {
- case err := <-f.notify.Errors():
- f.t.Fatal(err)
- case event := <-f.notify.Events():
- if strings.Contains(event.Name, syncPath) {
- break F
- }
- if strings.Contains(event.Name, anySyncPath) {
- continue
- }
- f.events = append(f.events, event)
- case <-timeout:
- f.t.Fatalf("fsync: timeout")
- }
- }
- if err != nil {
- f.t.Fatal(err)
- }
- }
- func (f *notifyFixture) tearDown() {
- SetLimitChecksEnabled(true)
- err := f.root.TearDown()
- if err != nil {
- f.t.Fatal(err)
- }
- err = f.notify.Close()
- if err != nil {
- f.t.Fatal(err)
- }
- }
|