OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / rjeczalik / notify / testing_test.go
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.
4
5 package notify
6
7 import (
8         "bufio"
9         "fmt"
10         "io/ioutil"
11         "os"
12         "path/filepath"
13         "reflect"
14         "runtime"
15         "sort"
16         "strconv"
17         "strings"
18         "testing"
19         "time"
20 )
21
22 // NOTE(rjeczalik): some useful environment variables:
23 //
24 //   - NOTIFY_DEBUG gives some extra information about generated events
25 //   - NOTIFY_TIMEOUT allows for changing default wait time for watcher's
26 //     events
27 //   - NOTIFY_TMP allows for changing location of temporary directory trees
28 //     created for test purpose
29
30 var wd string
31
32 func init() {
33         var err error
34         if wd, err = os.Getwd(); err != nil {
35                 panic("Getwd()=" + err.Error())
36         }
37 }
38
39 func timeout() time.Duration {
40         if s := os.Getenv("NOTIFY_TIMEOUT"); s != "" {
41                 if t, err := time.ParseDuration(s); err == nil {
42                         return t
43                 }
44         }
45         return 2 * time.Second
46 }
47
48 func vfs() (string, string) {
49         if s := os.Getenv("NOTIFY_TMP"); s != "" {
50                 return filepath.Split(s)
51         }
52         return "testdata", ""
53 }
54
55 func isDir(path string) bool {
56         r := path[len(path)-1]
57         return r == '\\' || r == '/'
58 }
59
60 func tmpcreateall(tmp string, path string) error {
61         isdir := isDir(path)
62         path = filepath.Join(tmp, filepath.FromSlash(path))
63         if isdir {
64                 if err := os.MkdirAll(path, 0755); err != nil {
65                         return err
66                 }
67         } else {
68                 if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
69                         return err
70                 }
71                 f, err := os.Create(path)
72                 if err != nil {
73                         return err
74                 }
75                 if err := nonil(f.Sync(), f.Close()); err != nil {
76                         return err
77                 }
78         }
79         return nil
80 }
81
82 func tmpcreate(root, path string) (bool, error) {
83         isdir := isDir(path)
84         path = filepath.Join(root, filepath.FromSlash(path))
85         if isdir {
86                 if err := os.Mkdir(path, 0755); err != nil {
87                         return false, err
88                 }
89         } else {
90                 f, err := os.Create(path)
91                 if err != nil {
92                         return false, err
93                 }
94                 if err := nonil(f.Sync(), f.Close()); err != nil {
95                         return false, err
96                 }
97         }
98         return isdir, nil
99 }
100
101 func tmptree(root, list string) (string, error) {
102         f, err := os.Open(list)
103         if err != nil {
104                 return "", err
105         }
106         defer f.Close()
107         if root == "" {
108                 if root, err = ioutil.TempDir(vfs()); err != nil {
109                         return "", err
110                 }
111         }
112         scanner := bufio.NewScanner(f)
113         for scanner.Scan() {
114                 if err := tmpcreateall(root, scanner.Text()); err != nil {
115                         return "", err
116                 }
117         }
118         if err := scanner.Err(); err != nil {
119                 return "", err
120         }
121         return root, nil
122 }
123
124 func callern(n int) string {
125         _, file, line, ok := runtime.Caller(n)
126         if !ok {
127                 return "<unknown>"
128         }
129         return filepath.Base(file) + ":" + strconv.Itoa(line)
130 }
131
132 func caller() string {
133         return callern(3)
134 }
135
136 type WCase struct {
137         Action func()
138         Events []EventInfo
139 }
140
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()))
145         }
146         return strings.Join(s, ", ")
147 }
148
149 type W struct {
150         Watcher watcher
151         C       chan EventInfo
152         Timeout time.Duration
153
154         t    *testing.T
155         root string
156 }
157
158 func newWatcherTest(t *testing.T, tree string) *W {
159         root, err := tmptree("", filepath.FromSlash(tree))
160         if err != nil {
161                 t.Fatalf(`tmptree("", %q)=%v`, tree, err)
162         }
163         Sync()
164         return &W{
165                 t:    t,
166                 root: root,
167         }
168 }
169
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}
174         }
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)
178                 }
179         } else {
180                 fn := func(path string, fi os.FileInfo, err error) error {
181                         if err != nil {
182                                 return err
183                         }
184                         if fi.IsDir() {
185                                 if err := w.watcher().Watch(path, joinevents(events)); err != nil {
186                                         return err
187                                 }
188                         }
189                         return nil
190                 }
191                 if err := filepath.Walk(w.root, fn); err != nil {
192                         t.Fatalf("Walk(%q, fn)=%v", w.root, err)
193                 }
194         }
195         drainall(w.C)
196         return w
197 }
198
199 func (w *W) clean(path string) string {
200         path, isrec, err := cleanpath(filepath.Join(w.root, path))
201         if err != nil {
202                 w.Fatalf("cleanpath(%q)=%v", path, err)
203         }
204         if isrec {
205                 path = path + "..."
206         }
207         return path
208 }
209
210 func (w *W) Fatal(v interface{}) {
211         w.t.Fatalf("%s: %v", caller(), v)
212 }
213
214 func (w *W) Fatalf(format string, v ...interface{}) {
215         w.t.Fatalf("%s: %s", caller(), fmt.Sprintf(format, v...))
216 }
217
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)
221         }
222 }
223
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)
227         }
228 }
229
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)
233         }
234 }
235
236 func (w *W) RecursiveWatch(path string, e Event) {
237         rw, ok := w.watcher().(recursiveWatcher)
238         if !ok {
239                 w.Fatal("watcher does not implement recursive watching on this platform")
240         }
241         if err := rw.RecursiveWatch(w.clean(path), e); err != nil {
242                 w.Fatalf("RecursiveWatch(%s, %v)=%v", path, e, err)
243         }
244 }
245
246 func (w *W) RecursiveRewatch(oldp, newp string, olde, newe Event) {
247         rw, ok := w.watcher().(recursiveWatcher)
248         if !ok {
249                 w.Fatal("watcher does not implement recursive watching on this platform")
250         }
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)
253         }
254 }
255
256 func (w *W) RecursiveUnwatch(path string) {
257         rw, ok := w.watcher().(recursiveWatcher)
258         if !ok {
259                 w.Fatal("watcher does not implement recursive watching on this platform")
260         }
261         if err := rw.RecursiveUnwatch(w.clean(path)); err != nil {
262                 w.Fatalf("RecursiveUnwatch(%s)=%v", path, err)
263         }
264 }
265
266 func (w *W) initwatcher(buffer int) {
267         c := make(chan EventInfo, buffer)
268         w.Watcher = newWatcher(c)
269         w.C = c
270 }
271
272 func (w *W) watcher() watcher {
273         if w.Watcher == nil {
274                 w.initwatcher(512)
275         }
276         return w.Watcher
277 }
278
279 func (w *W) c() chan EventInfo {
280         if w.C == nil {
281                 w.initwatcher(512)
282         }
283         return w.C
284 }
285
286 func (w *W) timeout() time.Duration {
287         if w.Timeout != 0 {
288                 return w.Timeout
289         }
290         return timeout()
291 }
292
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)
297         }
298         return nil
299 }
300
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())
305         }
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(),
309                         want.Event())
310         }
311         return nil
312 }
313
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,
317                         got, p)
318         }
319         return nil
320 }
321
322 func EqualCall(want, got Call) error {
323         if want.F != got.F {
324                 return fmt.Errorf("want F=%v; got %v (want.P=%q, got.P=%q)", want.F, got.F, want.P, got.P)
325         }
326         if got.E != want.E {
327                 return fmt.Errorf("want E=%v; got %v (want.P=%q, got.P=%q)", want.E, got.E, want.P, got.P)
328         }
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)
331         }
332         if want.C != got.C {
333                 return fmt.Errorf("want C=%p; got %p (want.P=%q, got.P=%q)", want.C, got.C, want.P, got.P)
334         }
335         if want := filepath.FromSlash(want.P); !strings.HasSuffix(got.P, want) {
336                 return fmt.Errorf("want P=%s; got %s", want, got.P)
337         }
338         if want := filepath.FromSlash(want.NP); !strings.HasSuffix(got.NP, want) {
339                 return fmt.Errorf("want NP=%s; got %s", want, got.NP)
340         }
341         return nil
342 }
343
344 func create(w *W, path string) WCase {
345         return WCase{
346                 Action: func() {
347                         isdir, err := tmpcreate(w.root, filepath.FromSlash(path))
348                         if err != nil {
349                                 w.Fatalf("tmpcreate(%q, %q)=%v", w.root, path, err)
350                         }
351                         if isdir {
352                                 dbgprintf("[FS] os.Mkdir(%q)\n", path)
353                         } else {
354                                 dbgprintf("[FS] os.Create(%q)\n", path)
355                         }
356                 },
357                 Events: []EventInfo{
358                         &Call{P: path, E: Create},
359                 },
360         }
361 }
362
363 func remove(w *W, path string) WCase {
364         return WCase{
365                 Action: func() {
366                         if err := os.RemoveAll(filepath.Join(w.root, filepath.FromSlash(path))); err != nil {
367                                 w.Fatal(err)
368                         }
369                         dbgprintf("[FS] os.Remove(%q)\n", path)
370                 },
371                 Events: []EventInfo{
372                         &Call{P: path, E: Remove},
373                 },
374         }
375 }
376
377 func rename(w *W, oldpath, newpath string) WCase {
378         return WCase{
379                 Action: func() {
380                         err := os.Rename(filepath.Join(w.root, filepath.FromSlash(oldpath)),
381                                 filepath.Join(w.root, filepath.FromSlash(newpath)))
382                         if err != nil {
383                                 w.Fatal(err)
384                         }
385                         dbgprintf("[FS] os.Rename(%q, %q)\n", oldpath, newpath)
386                 },
387                 Events: []EventInfo{
388                         &Call{P: newpath, E: Rename},
389                 },
390         }
391 }
392
393 func write(w *W, path string, p []byte) WCase {
394         return WCase{
395                 Action: func() {
396                         f, err := os.OpenFile(filepath.Join(w.root, filepath.FromSlash(path)),
397                                 os.O_WRONLY, 0644)
398                         if err != nil {
399                                 w.Fatalf("OpenFile(%q)=%v", path, err)
400                         }
401                         if _, err := f.Write(p); err != nil {
402                                 w.Fatalf("Write(%q)=%v", path, err)
403                         }
404                         if err := nonil(f.Sync(), f.Close()); err != nil {
405                                 w.Fatalf("Sync(%q)/Close(%q)=%v", path, path, err)
406                         }
407                         dbgprintf("[FS] Write(%q)\n", path)
408                 },
409                 Events: []EventInfo{
410                         &Call{P: path, E: Write},
411                 },
412         }
413 }
414
415 func drainall(c chan EventInfo) (ei []EventInfo) {
416         time.Sleep(50 * time.Millisecond)
417         for {
418                 select {
419                 case e := <-c:
420                         ei = append(ei, e)
421                         runtime.Gosched()
422                 default:
423                         return
424                 }
425         }
426 }
427
428 type WCaseFunc func(i int, cas WCase, ei EventInfo) error
429
430 func (w *W) ExpectAnyFunc(cases []WCase, fn WCaseFunc) {
431         UpdateWait() // Wait some time before starting the test.
432 Test:
433         for i, cas := range cases {
434                 dbgprintf("ExpectAny: i=%d\n", i)
435                 cas.Action()
436                 Sync()
437                 switch cas.Events {
438                 case nil:
439                         if ei := drainall(w.C); len(ei) != 0 {
440                                 w.Fatalf("unexpected dangling events: %v (i=%d)", ei, i)
441                         }
442                 default:
443                         select {
444                         case ei := <-w.C:
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 {
449                                                 dbgprint(err, j)
450                                                 continue
451                                         }
452                                         if fn != nil {
453                                                 if err := fn(i, cas, ei); err != nil {
454                                                         w.Fatalf("ExpectAnyFunc(%d, %v)=%v", i, ei, err)
455                                                 }
456                                         }
457                                         drainall(w.C) // TODO(rjeczalik): revisit
458                                         continue Test
459                                 }
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(),
464                                         cas.Events, i)
465                         }
466                         drainall(w.C) // TODO(rjeczalik): revisit
467                 }
468         }
469 }
470
471 func (w *W) ExpectAny(cases []WCase) {
472         w.ExpectAnyFunc(cases, nil)
473 }
474
475 func (w *W) aggregate(ei []EventInfo, pf string) (evs map[string]Event) {
476         evs = make(map[string]Event)
477         for _, cas := range ei {
478                 p := cas.Path()
479                 if pf != "" {
480                         p = filepath.Join(pf, p)
481                 }
482                 evs[p] |= cas.Event()
483         }
484         return
485 }
486
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)
492                 cas.Action()
493                 Sync()
494                 got := w.aggregate(drainall(w.C), "")
495                 for ep, ee := range exp {
496                         ge, ok := got[ep]
497                         if !ok {
498                                 w.Fatalf("missing events for %q (%v)", ep, ee)
499                                 continue
500                         }
501                         delete(got, ep)
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)
505                                 continue
506                         }
507                 }
508                 if len(got) != 0 {
509                         w.Fatalf("ExpectAll received unexpected events: %v", got)
510                 }
511         }
512 }
513
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)
520 }
521
522 // FuncType represents enums for Watcher interface.
523 type FuncType string
524
525 const (
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")
533 )
534
535 type Chans []chan EventInfo
536
537 func NewChans(n int) Chans {
538         ch := make([]chan EventInfo, n)
539         for i := range ch {
540                 ch[i] = make(chan EventInfo, buffer)
541         }
542         return ch
543 }
544
545 func (c Chans) Foreach(fn func(chan<- EventInfo, node)) {
546         for i, ch := range c {
547                 fn(ch, node{Name: strconv.Itoa(i)})
548         }
549 }
550
551 func (c Chans) Drain() (ei []EventInfo) {
552         n := len(c)
553         stop := make(chan struct{})
554         eich := make(chan EventInfo, n*buffer)
555         go func() {
556                 defer close(eich)
557                 cases := make([]reflect.SelectCase, n+1)
558                 for i := range c {
559                         cases[i].Chan = reflect.ValueOf(c[i])
560                         cases[i].Dir = reflect.SelectRecv
561                 }
562                 cases[n].Chan = reflect.ValueOf(stop)
563                 cases[n].Dir = reflect.SelectRecv
564                 for {
565                         i, v, ok := reflect.Select(cases)
566                         if i == n {
567                                 return
568                         }
569                         if !ok {
570                                 panic("(Chans).Drain(): unexpected chan close")
571                         }
572                         eich <- v.Interface().(EventInfo)
573                 }
574         }()
575         <-time.After(50 * time.Duration(n) * time.Millisecond)
576         close(stop)
577         for e := range eich {
578                 ei = append(ei, e)
579         }
580         return
581 }
582
583 // Call represents single call to Watcher issued by the Tree
584 // and recorded by a spy Watcher mock.
585 type Call struct {
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()
594 }
595
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 }
602
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
606
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) }
612
613 // Spy is a mock for Watcher interface, which records every call.
614 type Spy []Call
615
616 func (s *Spy) Close() (_ error) { return }
617
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})
621         return
622 }
623
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})
627         return
628 }
629
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})
633         return
634 }
635
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})
639         return
640 }
641
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})
645         return
646 }
647
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})
651         return
652 }
653
654 type RCase struct {
655         Call   Call
656         Record []Call
657 }
658
659 type TCase struct {
660         Event    Call
661         Receiver Chans
662 }
663
664 type NCase struct {
665         Event    WCase
666         Receiver Chans
667 }
668
669 type N struct {
670         Timeout time.Duration
671
672         t    *testing.T
673         tree tree
674         w    *W
675         spy  *Spy
676         c    chan EventInfo
677         j    int // spy offset
678
679         realroot string
680 }
681
682 func newN(t *testing.T, tree string) *N {
683         n := &N{
684                 t: t,
685                 w: newWatcherTest(t, tree),
686         }
687         realroot, err := canonical(n.w.root)
688         if err != nil {
689                 t.Fatalf("%s: unexpected fixture failure: %v", caller(), err)
690         }
691         n.realroot = realroot
692         return n
693 }
694
695 func newTreeN(t *testing.T, tree string) *N {
696         c := make(chan EventInfo, buffer)
697         n := newN(t, tree)
698         n.spy = &Spy{}
699         n.w.Watcher = n.spy
700         n.w.C = c
701         n.c = c
702         return n
703 }
704
705 func NewNotifyTest(t *testing.T, tree string) *N {
706         n := newN(t, tree)
707         if rw, ok := n.w.watcher().(recursiveWatcher); ok {
708                 n.tree = newRecursiveTree(rw, n.w.c())
709         } else {
710                 n.tree = newNonrecursiveTree(n.w.watcher(), n.w.c(), nil)
711         }
712         return n
713 }
714
715 func NewRecursiveTreeTest(t *testing.T, tree string) *N {
716         n := newTreeN(t, tree)
717         n.tree = newRecursiveTree(n.spy, n.c)
718         return n
719 }
720
721 func NewNonrecursiveTreeTest(t *testing.T, tree string) *N {
722         n := newTreeN(t, tree)
723         n.tree = newNonrecursiveTree(n.spy, n.c, nil)
724         return n
725 }
726
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)
731         go func() {
732                 for ei := range rec {
733                         select {
734                         case recinternal <- ei:
735                         default:
736                                 t.Fatalf("failed to send ei to recinternal: not ready")
737                         }
738                         select {
739                         case recuser <- ei:
740                         default:
741                                 t.Fatalf("failed to send ei to recuser: not ready")
742                         }
743                 }
744         }()
745         n := newTreeN(t, tree)
746         tr := newNonrecursiveTree(n.spy, n.c, recinternal)
747         tr.rec = rec
748         n.tree = tr
749         return n, recuser
750 }
751
752 func (n *N) timeout() time.Duration {
753         if n.Timeout != 0 {
754                 return n.Timeout
755         }
756         return n.w.timeout()
757 }
758
759 func (n *N) W() *W {
760         return n.w
761 }
762
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)
767         }
768         return nil
769 }
770
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)
776         }
777 }
778
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)
786         }
787 }
788
789 func (n *N) Stop(c chan<- EventInfo) {
790         n.tree.Stop(c)
791 }
792
793 func (n *N) Call(calls ...Call) {
794         for i := range calls {
795                 switch calls[i].F {
796                 case FuncWatch:
797                         n.Watch(calls[i].P, calls[i].C, calls[i].E)
798                 case FuncStop:
799                         n.Stop(calls[i].C)
800                 default:
801                         panic("unsupported call type: " + string(calls[i].F))
802                 }
803         }
804 }
805
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)
809         }
810 }
811
812 func (n *N) ExpectRecordedCalls(cases []RCase) {
813         for i, cas := range cases {
814                 dbgprintf("ExpectRecordedCalls: i=%d\n", i)
815                 n.Call(cas.Call)
816                 record := (*n.spy)[n.j:]
817                 if len(cas.Record) == 0 && len(record) == 0 {
818                         continue
819                 }
820                 n.j = len(*n.spy)
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)
824                 }
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)
829                         }
830                 }
831         }
832 }
833
834 func (n *N) collect(ch Chans) <-chan []EventInfo {
835         done := make(chan []EventInfo)
836         go func() {
837                 cases := make([]reflect.SelectCase, len(ch))
838                 unique := make(map[<-chan EventInfo]EventInfo, len(ch))
839                 for i := range ch {
840                         cases[i].Chan = reflect.ValueOf(ch[i])
841                         cases[i].Dir = reflect.SelectRecv
842                 }
843                 for i := len(cases); i != 0; i = len(cases) {
844                         j, v, ok := reflect.Select(cases)
845                         if !ok {
846                                 n.t.Fatal("unexpected chan close")
847                         }
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)
852                         }
853                         unique[ch] = got
854                         cases[j], cases = cases[i-1], cases[:i-1]
855                 }
856                 collected := make([]EventInfo, 0, len(ch))
857                 for _, ch := range unique {
858                         collected = append(collected, ch)
859                 }
860                 done <- collected
861         }()
862         return done
863 }
864
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)
869         }
870         return &rel
871 }
872
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.
877                 n.expectDry(all, i)
878                 n.c <- n.abs(cas.Event)
879                 switch cas.Receiver {
880                 case nil:
881                         n.expectDry(all, i)
882                 default:
883                         ch := n.collect(cas.Receiver)
884                         select {
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)
889                                         }
890                                 }
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)
894                         }
895
896                 }
897         }
898         n.expectDry(all, -1)
899 }
900
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)
905                 cas.Event.Action()
906                 Sync()
907                 switch cas.Receiver {
908                 case nil:
909                         n.expectDry(all, i)
910                 default:
911                         ch := n.collect(cas.Receiver)
912                         select {
913                         case collected := <-ch:
914                         Compare:
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 {
920                                                         dbgprint(err, j)
921                                                         continue
922                                                 }
923                                                 continue Compare
924                                         }
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)
928                                 }
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)
932                         }
933                 }
934         }
935         n.expectDry(all, -1)
936 }
937
938 func (n *N) Walk(fn walkFunc) {
939         switch t := n.tree.(type) {
940         case *recursiveTree:
941                 if err := t.root.Walk("", fn); err != nil {
942                         n.w.Fatal(err)
943                 }
944         case *nonrecursiveTree:
945                 if err := t.root.Walk("", fn); err != nil {
946                         n.w.Fatal(err)
947                 }
948         default:
949                 n.t.Fatal("unknown tree type")
950         }
951 }