1 // Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
2 // Use of this source code is governed by the MIT license that can be
3 // found in the LICENSE file.
22 // NOTE(rjeczalik): some useful environment variables:
24 // - NOTIFY_DEBUG gives some extra information about generated events
25 // - NOTIFY_TIMEOUT allows for changing default wait time for watcher's
27 // - NOTIFY_TMP allows for changing location of temporary directory trees
28 // created for test purpose
34 if wd, err = os.Getwd(); err != nil {
35 panic("Getwd()=" + err.Error())
39 func timeout() time.Duration {
40 if s := os.Getenv("NOTIFY_TIMEOUT"); s != "" {
41 if t, err := time.ParseDuration(s); err == nil {
45 return 2 * time.Second
48 func vfs() (string, string) {
49 if s := os.Getenv("NOTIFY_TMP"); s != "" {
50 return filepath.Split(s)
55 func isDir(path string) bool {
56 r := path[len(path)-1]
57 return r == '\\' || r == '/'
60 func tmpcreateall(tmp string, path string) error {
62 path = filepath.Join(tmp, filepath.FromSlash(path))
64 if err := os.MkdirAll(path, 0755); err != nil {
68 if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
71 f, err := os.Create(path)
75 if err := nonil(f.Sync(), f.Close()); err != nil {
82 func tmpcreate(root, path string) (bool, error) {
84 path = filepath.Join(root, filepath.FromSlash(path))
86 if err := os.Mkdir(path, 0755); err != nil {
90 f, err := os.Create(path)
94 if err := nonil(f.Sync(), f.Close()); err != nil {
101 func tmptree(root, list string) (string, error) {
102 f, err := os.Open(list)
108 if root, err = ioutil.TempDir(vfs()); err != nil {
112 scanner := bufio.NewScanner(f)
114 if err := tmpcreateall(root, scanner.Text()); err != nil {
118 if err := scanner.Err(); err != nil {
124 func callern(n int) string {
125 _, file, line, ok := runtime.Caller(n)
129 return filepath.Base(file) + ":" + strconv.Itoa(line)
132 func caller() string {
141 func (cas WCase) String() string {
142 s := make([]string, 0, len(cas.Events))
143 for _, ei := range cas.Events {
144 s = append(s, "Event("+ei.Event().String()+")@"+filepath.FromSlash(ei.Path()))
146 return strings.Join(s, ", ")
152 Timeout time.Duration
158 func newWatcherTest(t *testing.T, tree string) *W {
159 root, err := tmptree("", filepath.FromSlash(tree))
161 t.Fatalf(`tmptree("", %q)=%v`, tree, err)
170 func NewWatcherTest(t *testing.T, tree string, events ...Event) *W {
171 w := newWatcherTest(t, tree)
172 if len(events) == 0 {
173 events = []Event{Create, Remove, Write, Rename}
175 if rw, ok := w.watcher().(recursiveWatcher); ok {
176 if err := rw.RecursiveWatch(w.root, joinevents(events)); err != nil {
177 t.Fatalf("RecursiveWatch(%q, All)=%v", w.root, err)
180 fn := func(path string, fi os.FileInfo, err error) error {
185 if err := w.watcher().Watch(path, joinevents(events)); err != nil {
191 if err := filepath.Walk(w.root, fn); err != nil {
192 t.Fatalf("Walk(%q, fn)=%v", w.root, err)
199 func (w *W) clean(path string) string {
200 path, isrec, err := cleanpath(filepath.Join(w.root, path))
202 w.Fatalf("cleanpath(%q)=%v", path, err)
210 func (w *W) Fatal(v interface{}) {
211 w.t.Fatalf("%s: %v", caller(), v)
214 func (w *W) Fatalf(format string, v ...interface{}) {
215 w.t.Fatalf("%s: %s", caller(), fmt.Sprintf(format, v...))
218 func (w *W) Watch(path string, e Event) {
219 if err := w.watcher().Watch(w.clean(path), e); err != nil {
220 w.Fatalf("Watch(%s, %v)=%v", path, e, err)
224 func (w *W) Rewatch(path string, olde, newe Event) {
225 if err := w.watcher().Rewatch(w.clean(path), olde, newe); err != nil {
226 w.Fatalf("Rewatch(%s, %v, %v)=%v", path, olde, newe, err)
230 func (w *W) Unwatch(path string) {
231 if err := w.watcher().Unwatch(w.clean(path)); err != nil {
232 w.Fatalf("Unwatch(%s)=%v", path, err)
236 func (w *W) RecursiveWatch(path string, e Event) {
237 rw, ok := w.watcher().(recursiveWatcher)
239 w.Fatal("watcher does not implement recursive watching on this platform")
241 if err := rw.RecursiveWatch(w.clean(path), e); err != nil {
242 w.Fatalf("RecursiveWatch(%s, %v)=%v", path, e, err)
246 func (w *W) RecursiveRewatch(oldp, newp string, olde, newe Event) {
247 rw, ok := w.watcher().(recursiveWatcher)
249 w.Fatal("watcher does not implement recursive watching on this platform")
251 if err := rw.RecursiveRewatch(w.clean(oldp), w.clean(newp), olde, newe); err != nil {
252 w.Fatalf("RecursiveRewatch(%s, %s, %v, %v)=%v", oldp, newp, olde, newe, err)
256 func (w *W) RecursiveUnwatch(path string) {
257 rw, ok := w.watcher().(recursiveWatcher)
259 w.Fatal("watcher does not implement recursive watching on this platform")
261 if err := rw.RecursiveUnwatch(w.clean(path)); err != nil {
262 w.Fatalf("RecursiveUnwatch(%s)=%v", path, err)
266 func (w *W) initwatcher(buffer int) {
267 c := make(chan EventInfo, buffer)
268 w.Watcher = newWatcher(c)
272 func (w *W) watcher() watcher {
273 if w.Watcher == nil {
279 func (w *W) c() chan EventInfo {
286 func (w *W) timeout() time.Duration {
293 func (w *W) Close() error {
294 defer os.RemoveAll(w.root)
295 if err := w.watcher().Close(); err != nil {
296 w.Fatalf("w.Watcher.Close()=%v", err)
301 func EqualEventInfo(want, got EventInfo) error {
302 if got.Event() != want.Event() {
303 return fmt.Errorf("want Event()=%v; got %v (path=%s)", want.Event(),
304 got.Event(), want.Path())
306 path := strings.TrimRight(filepath.FromSlash(want.Path()), `/\`)
307 if !strings.HasSuffix(got.Path(), path) {
308 return fmt.Errorf("want Path()=%s; got %s (event=%v)", path, got.Path(),
314 func HasEventInfo(want, got Event, p string) error {
315 if got&want != want {
316 return fmt.Errorf("want Event=%v; got %v (path=%s)", want,
322 func EqualCall(want, got Call) error {
324 return fmt.Errorf("want F=%v; got %v (want.P=%q, got.P=%q)", want.F, got.F, want.P, got.P)
327 return fmt.Errorf("want E=%v; got %v (want.P=%q, got.P=%q)", want.E, got.E, want.P, got.P)
329 if got.NE != want.NE {
330 return fmt.Errorf("want NE=%v; got %v (want.P=%q, got.P=%q)", want.NE, got.NE, want.P, got.P)
333 return fmt.Errorf("want C=%p; got %p (want.P=%q, got.P=%q)", want.C, got.C, want.P, got.P)
335 if want := filepath.FromSlash(want.P); !strings.HasSuffix(got.P, want) {
336 return fmt.Errorf("want P=%s; got %s", want, got.P)
338 if want := filepath.FromSlash(want.NP); !strings.HasSuffix(got.NP, want) {
339 return fmt.Errorf("want NP=%s; got %s", want, got.NP)
344 func create(w *W, path string) WCase {
347 isdir, err := tmpcreate(w.root, filepath.FromSlash(path))
349 w.Fatalf("tmpcreate(%q, %q)=%v", w.root, path, err)
352 dbgprintf("[FS] os.Mkdir(%q)\n", path)
354 dbgprintf("[FS] os.Create(%q)\n", path)
358 &Call{P: path, E: Create},
363 func remove(w *W, path string) WCase {
366 if err := os.RemoveAll(filepath.Join(w.root, filepath.FromSlash(path))); err != nil {
369 dbgprintf("[FS] os.Remove(%q)\n", path)
372 &Call{P: path, E: Remove},
377 func rename(w *W, oldpath, newpath string) WCase {
380 err := os.Rename(filepath.Join(w.root, filepath.FromSlash(oldpath)),
381 filepath.Join(w.root, filepath.FromSlash(newpath)))
385 dbgprintf("[FS] os.Rename(%q, %q)\n", oldpath, newpath)
388 &Call{P: newpath, E: Rename},
393 func write(w *W, path string, p []byte) WCase {
396 f, err := os.OpenFile(filepath.Join(w.root, filepath.FromSlash(path)),
399 w.Fatalf("OpenFile(%q)=%v", path, err)
401 if _, err := f.Write(p); err != nil {
402 w.Fatalf("Write(%q)=%v", path, err)
404 if err := nonil(f.Sync(), f.Close()); err != nil {
405 w.Fatalf("Sync(%q)/Close(%q)=%v", path, path, err)
407 dbgprintf("[FS] Write(%q)\n", path)
410 &Call{P: path, E: Write},
415 func drainall(c chan EventInfo) (ei []EventInfo) {
416 time.Sleep(50 * time.Millisecond)
428 type WCaseFunc func(i int, cas WCase, ei EventInfo) error
430 func (w *W) ExpectAnyFunc(cases []WCase, fn WCaseFunc) {
431 UpdateWait() // Wait some time before starting the test.
433 for i, cas := range cases {
434 dbgprintf("ExpectAny: i=%d\n", i)
439 if ei := drainall(w.C); len(ei) != 0 {
440 w.Fatalf("unexpected dangling events: %v (i=%d)", ei, i)
445 dbgprintf("received: path=%q, event=%v, sys=%v (i=%d)", ei.Path(),
446 ei.Event(), ei.Sys(), i)
447 for j, want := range cas.Events {
448 if err := EqualEventInfo(want, ei); err != nil {
453 if err := fn(i, cas, ei); err != nil {
454 w.Fatalf("ExpectAnyFunc(%d, %v)=%v", i, ei, err)
457 drainall(w.C) // TODO(rjeczalik): revisit
460 w.Fatalf("ExpectAny received an event which does not match any of "+
461 "the expected ones (i=%d): want one of %v; got %v", i, cas.Events, ei)
462 case <-time.After(w.timeout()):
463 w.Fatalf("timed out after %v waiting for one of %v (i=%d)", w.timeout(),
466 drainall(w.C) // TODO(rjeczalik): revisit
471 func (w *W) ExpectAny(cases []WCase) {
472 w.ExpectAnyFunc(cases, nil)
475 func (w *W) aggregate(ei []EventInfo, pf string) (evs map[string]Event) {
476 evs = make(map[string]Event)
477 for _, cas := range ei {
480 p = filepath.Join(pf, p)
482 evs[p] |= cas.Event()
487 func (w *W) ExpectAllFunc(cases []WCase) {
488 UpdateWait() // Wait some time before starting the test.
489 for i, cas := range cases {
490 exp := w.aggregate(cas.Events, w.root)
491 dbgprintf("ExpectAll: i=%d\n", i)
494 got := w.aggregate(drainall(w.C), "")
495 for ep, ee := range exp {
498 w.Fatalf("missing events for %q (%v)", ep, ee)
502 if err := HasEventInfo(ee, ge, ep); err != nil {
503 w.Fatalf("ExpectAll received an event which does not match "+
504 "the expected ones for %q: want %v; got %v", ep, ee, ge)
509 w.Fatalf("ExpectAll received unexpected events: %v", got)
514 // ExpectAll requires all requested events to be send.
515 // It does not require events to be send in the same order or in the same
516 // chunks (e.g. NoteWrite and NoteExtend reported as independent events are
517 // treated the same as one NoteWrite|NoteExtend event).
518 func (w *W) ExpectAll(cases []WCase) {
519 w.ExpectAllFunc(cases)
522 // FuncType represents enums for Watcher interface.
526 FuncWatch = FuncType("Watch")
527 FuncUnwatch = FuncType("Unwatch")
528 FuncRewatch = FuncType("Rewatch")
529 FuncRecursiveWatch = FuncType("RecursiveWatch")
530 FuncRecursiveUnwatch = FuncType("RecursiveUnwatch")
531 FuncRecursiveRewatch = FuncType("RecursiveRewatch")
532 FuncStop = FuncType("Stop")
535 type Chans []chan EventInfo
537 func NewChans(n int) Chans {
538 ch := make([]chan EventInfo, n)
540 ch[i] = make(chan EventInfo, buffer)
545 func (c Chans) Foreach(fn func(chan<- EventInfo, node)) {
546 for i, ch := range c {
547 fn(ch, node{Name: strconv.Itoa(i)})
551 func (c Chans) Drain() (ei []EventInfo) {
553 stop := make(chan struct{})
554 eich := make(chan EventInfo, n*buffer)
557 cases := make([]reflect.SelectCase, n+1)
559 cases[i].Chan = reflect.ValueOf(c[i])
560 cases[i].Dir = reflect.SelectRecv
562 cases[n].Chan = reflect.ValueOf(stop)
563 cases[n].Dir = reflect.SelectRecv
565 i, v, ok := reflect.Select(cases)
570 panic("(Chans).Drain(): unexpected chan close")
572 eich <- v.Interface().(EventInfo)
575 <-time.After(50 * time.Duration(n) * time.Millisecond)
577 for e := range eich {
583 // Call represents single call to Watcher issued by the Tree
584 // and recorded by a spy Watcher mock.
586 F FuncType // denotes type of function to call, for both watcher and notifier interface
587 C chan EventInfo // user channel being an argument to either Watch or Stop function
588 P string // regular Path argument and old path from RecursiveRewatch call
589 NP string // new Path argument from RecursiveRewatch call
590 E Event // regular Event argument and old Event from a Rewatch call
591 NE Event // new Event argument from Rewatch call
592 S interface{} // when Call is used as EventInfo, S is a value of Sys()
593 Dir bool // when Call is used as EventInfo, Dir is a value of isDir()
596 // Call implements the EventInfo interface.
597 func (c *Call) Event() Event { return c.E }
598 func (c *Call) Path() string { return c.P }
599 func (c *Call) String() string { return fmt.Sprintf("%#v", c) }
600 func (c *Call) Sys() interface{} { return c.S }
601 func (c *Call) isDir() (bool, error) { return c.Dir, nil }
603 // CallSlice is a convenient wrapper for a slice of Call values, which allows
604 // to sort them in ascending order.
605 type CallSlice []Call
607 // CallSlice implements sort.Interface inteface.
608 func (cs CallSlice) Len() int { return len(cs) }
609 func (cs CallSlice) Less(i, j int) bool { return cs[i].P < cs[j].P }
610 func (cs CallSlice) Swap(i, j int) { cs[i], cs[j] = cs[j], cs[i] }
611 func (cs CallSlice) Sort() { sort.Sort(cs) }
613 // Spy is a mock for Watcher interface, which records every call.
616 func (s *Spy) Close() (_ error) { return }
618 func (s *Spy) Watch(p string, e Event) (_ error) {
619 dbgprintf("%s: (*Spy).Watch(%q, %v)", caller(), p, e)
620 *s = append(*s, Call{F: FuncWatch, P: p, E: e})
624 func (s *Spy) Unwatch(p string) (_ error) {
625 dbgprintf("%s: (*Spy).Unwatch(%q)", caller(), p)
626 *s = append(*s, Call{F: FuncUnwatch, P: p})
630 func (s *Spy) Rewatch(p string, olde, newe Event) (_ error) {
631 dbgprintf("%s: (*Spy).Rewatch(%q, %v, %v)", caller(), p, olde, newe)
632 *s = append(*s, Call{F: FuncRewatch, P: p, E: olde, NE: newe})
636 func (s *Spy) RecursiveWatch(p string, e Event) (_ error) {
637 dbgprintf("%s: (*Spy).RecursiveWatch(%q, %v)", caller(), p, e)
638 *s = append(*s, Call{F: FuncRecursiveWatch, P: p, E: e})
642 func (s *Spy) RecursiveUnwatch(p string) (_ error) {
643 dbgprintf("%s: (*Spy).RecursiveUnwatch(%q)", caller(), p)
644 *s = append(*s, Call{F: FuncRecursiveUnwatch, P: p})
648 func (s *Spy) RecursiveRewatch(oldp, newp string, olde, newe Event) (_ error) {
649 dbgprintf("%s: (*Spy).RecursiveRewatch(%q, %q, %v, %v)", caller(), oldp, newp, olde, newe)
650 *s = append(*s, Call{F: FuncRecursiveRewatch, P: oldp, NP: newp, E: olde, NE: newe})
670 Timeout time.Duration
682 func newN(t *testing.T, tree string) *N {
685 w: newWatcherTest(t, tree),
687 realroot, err := canonical(n.w.root)
689 t.Fatalf("%s: unexpected fixture failure: %v", caller(), err)
691 n.realroot = realroot
695 func newTreeN(t *testing.T, tree string) *N {
696 c := make(chan EventInfo, buffer)
705 func NewNotifyTest(t *testing.T, tree string) *N {
707 if rw, ok := n.w.watcher().(recursiveWatcher); ok {
708 n.tree = newRecursiveTree(rw, n.w.c())
710 n.tree = newNonrecursiveTree(n.w.watcher(), n.w.c(), nil)
715 func NewRecursiveTreeTest(t *testing.T, tree string) *N {
716 n := newTreeN(t, tree)
717 n.tree = newRecursiveTree(n.spy, n.c)
721 func NewNonrecursiveTreeTest(t *testing.T, tree string) *N {
722 n := newTreeN(t, tree)
723 n.tree = newNonrecursiveTree(n.spy, n.c, nil)
727 func NewNonrecursiveTreeTestC(t *testing.T, tree string) (*N, chan EventInfo) {
728 rec := make(chan EventInfo, buffer)
729 recinternal := make(chan EventInfo, buffer)
730 recuser := make(chan EventInfo, buffer)
732 for ei := range rec {
734 case recinternal <- ei:
736 t.Fatalf("failed to send ei to recinternal: not ready")
741 t.Fatalf("failed to send ei to recuser: not ready")
745 n := newTreeN(t, tree)
746 tr := newNonrecursiveTree(n.spy, n.c, recinternal)
752 func (n *N) timeout() time.Duration {
763 func (n *N) Close() error {
764 defer os.RemoveAll(n.w.root)
765 if err := n.tree.Close(); err != nil {
766 n.w.Fatalf("(notifier).Close()=%v", err)
771 func (n *N) Watch(path string, c chan<- EventInfo, events ...Event) {
772 UpdateWait() // we need to wait on Windows because of its asynchronous watcher.
773 path = filepath.Join(n.w.root, path)
774 if err := n.tree.Watch(path, c, events...); err != nil {
775 n.t.Errorf("Watch(%s, %p, %v)=%v", path, c, events, err)
779 func (n *N) WatchErr(path string, c chan<- EventInfo, err error, events ...Event) {
780 path = filepath.Join(n.w.root, path)
781 switch e := n.tree.Watch(path, c, events...); {
782 case err == nil && e == nil:
783 n.t.Errorf("Watch(%s, %p, %v)=nil", path, c, events)
784 case err != nil && e != err:
785 n.t.Errorf("Watch(%s, %p, %v)=%v != %v", path, c, events, e, err)
789 func (n *N) Stop(c chan<- EventInfo) {
793 func (n *N) Call(calls ...Call) {
794 for i := range calls {
797 n.Watch(calls[i].P, calls[i].C, calls[i].E)
801 panic("unsupported call type: " + string(calls[i].F))
806 func (n *N) expectDry(ch Chans, i int) {
807 if ei := ch.Drain(); len(ei) != 0 {
808 n.w.Fatalf("unexpected dangling events: %v (i=%d)", ei, i)
812 func (n *N) ExpectRecordedCalls(cases []RCase) {
813 for i, cas := range cases {
814 dbgprintf("ExpectRecordedCalls: i=%d\n", i)
816 record := (*n.spy)[n.j:]
817 if len(cas.Record) == 0 && len(record) == 0 {
821 if len(record) != len(cas.Record) {
822 n.t.Fatalf("%s: want len(record)=%d; got %d [%+v] (i=%d)", caller(),
823 len(cas.Record), len(record), record, i)
825 CallSlice(record).Sort()
826 for j := range cas.Record {
827 if err := EqualCall(cas.Record[j], record[j]); err != nil {
828 n.t.Fatalf("%s: %v (i=%d, j=%d)", caller(), err, i, j)
834 func (n *N) collect(ch Chans) <-chan []EventInfo {
835 done := make(chan []EventInfo)
837 cases := make([]reflect.SelectCase, len(ch))
838 unique := make(map[<-chan EventInfo]EventInfo, len(ch))
840 cases[i].Chan = reflect.ValueOf(ch[i])
841 cases[i].Dir = reflect.SelectRecv
843 for i := len(cases); i != 0; i = len(cases) {
844 j, v, ok := reflect.Select(cases)
846 n.t.Fatal("unexpected chan close")
848 ch := cases[j].Chan.Interface().(chan EventInfo)
849 got := v.Interface().(EventInfo)
850 if ei, ok := unique[ch]; ok {
851 n.t.Fatalf("duplicated event %v (previous=%v) received on collect", got, ei)
854 cases[j], cases = cases[i-1], cases[:i-1]
856 collected := make([]EventInfo, 0, len(ch))
857 for _, ch := range unique {
858 collected = append(collected, ch)
865 func (n *N) abs(rel Call) *Call {
866 rel.P = filepath.Join(n.realroot, filepath.FromSlash(rel.P))
867 if !filepath.IsAbs(rel.P) {
868 rel.P = filepath.Join(wd, rel.P)
873 func (n *N) ExpectTreeEvents(cases []TCase, all Chans) {
874 for i, cas := range cases {
875 dbgprintf("ExpectTreeEvents: i=%d\n", i)
876 // Ensure there're no dangling event left by previous test-case.
878 n.c <- n.abs(cas.Event)
879 switch cas.Receiver {
883 ch := n.collect(cas.Receiver)
885 case collected := <-ch:
886 for _, got := range collected {
887 if err := EqualEventInfo(&cas.Event, got); err != nil {
888 n.w.Fatalf("%s: %s (i=%d)", caller(), err, i)
891 case <-time.After(n.timeout()):
892 n.w.Fatalf("ExpectTreeEvents has timed out after %v waiting for"+
893 " %v on %s (i=%d)", n.timeout(), cas.Event.E, cas.Event.P, i)
901 func (n *N) ExpectNotifyEvents(cases []NCase, all Chans) {
902 UpdateWait() // Wait some time before starting the test.
903 for i, cas := range cases {
904 dbgprintf("ExpectNotifyEvents: i=%d\n", i)
907 switch cas.Receiver {
911 ch := n.collect(cas.Receiver)
913 case collected := <-ch:
915 for j, ei := range collected {
916 dbgprintf("received: path=%q, event=%v, sys=%v (i=%d, j=%d)", ei.Path(),
917 ei.Event(), ei.Sys(), i, j)
918 for _, want := range cas.Event.Events {
919 if err := EqualEventInfo(want, ei); err != nil {
925 n.w.Fatalf("ExpectNotifyEvents received an event which does not"+
926 " match any of the expected ones (i=%d): want one of %v; got %v", i,
927 cas.Event.Events, ei)
929 case <-time.After(n.timeout()):
930 n.w.Fatalf("ExpectNotifyEvents did not receive any of the expected events [%v] "+
931 "after %v (i=%d)", cas.Event, n.timeout(), i)
938 func (n *N) Walk(fn walkFunc) {
939 switch t := n.tree.(type) {
941 if err := t.root.Walk("", fn); err != nil {
944 case *nonrecursiveTree:
945 if err := t.root.Walk("", fn); err != nil {
949 n.t.Fatal("unknown tree type")