github.com/DiversionCompany/notify@v0.9.9/testing_test.go (about)

     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  	"os"
    11  	"path/filepath"
    12  	"reflect"
    13  	"runtime"
    14  	"sort"
    15  	"strconv"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  )
    20  
    21  // NOTE(rjeczalik): some useful environment variables:
    22  //
    23  //   - NOTIFY_DEBUG gives some extra information about generated events
    24  //   - NOTIFY_TIMEOUT allows for changing default wait time for watcher's
    25  //     events
    26  //   - NOTIFY_TMP allows for changing location of temporary directory trees
    27  //     created for test purpose
    28  
    29  var wd string
    30  
    31  func init() {
    32  	var err error
    33  	if wd, err = os.Getwd(); err != nil {
    34  		panic("Getwd()=" + err.Error())
    35  	}
    36  }
    37  
    38  func timeout() time.Duration {
    39  	if s := os.Getenv("NOTIFY_TIMEOUT"); s != "" {
    40  		if t, err := time.ParseDuration(s); err == nil {
    41  			return t
    42  		}
    43  	}
    44  	return 2 * time.Second
    45  }
    46  
    47  func vfs() (string, string) {
    48  	if s := os.Getenv("NOTIFY_TMP"); s != "" {
    49  		return filepath.Split(s)
    50  	}
    51  	return "testdata", ""
    52  }
    53  
    54  func isDir(path string) bool {
    55  	r := path[len(path)-1]
    56  	return r == '\\' || r == '/'
    57  }
    58  
    59  func tmpcreateall(tmp string, path string) error {
    60  	isdir := isDir(path)
    61  	path = filepath.Join(tmp, filepath.FromSlash(path))
    62  	if isdir {
    63  		if err := os.MkdirAll(path, 0755); err != nil {
    64  			return err
    65  		}
    66  	} else {
    67  		if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
    68  			return err
    69  		}
    70  		f, err := os.Create(path)
    71  		if err != nil {
    72  			return err
    73  		}
    74  		if err := nonil(f.Sync(), f.Close()); err != nil {
    75  			return err
    76  		}
    77  	}
    78  	return nil
    79  }
    80  
    81  func tmpcreate(root, path string) (bool, error) {
    82  	isdir := isDir(path)
    83  	path = filepath.Join(root, filepath.FromSlash(path))
    84  	if isdir {
    85  		if err := os.Mkdir(path, 0755); err != nil {
    86  			return false, err
    87  		}
    88  	} else {
    89  		f, err := os.Create(path)
    90  		if err != nil {
    91  			return false, err
    92  		}
    93  		if err := nonil(f.Sync(), f.Close()); err != nil {
    94  			return false, err
    95  		}
    96  	}
    97  	return isdir, nil
    98  }
    99  
   100  func tmptree(root, list string) (string, error) {
   101  	f, err := os.Open(list)
   102  	if err != nil {
   103  		return "", err
   104  	}
   105  	defer f.Close()
   106  	if root == "" {
   107  		if root, err = os.MkdirTemp(vfs()); err != nil {
   108  			return "", err
   109  		}
   110  	}
   111  	scanner := bufio.NewScanner(f)
   112  	for scanner.Scan() {
   113  		if err := tmpcreateall(root, scanner.Text()); err != nil {
   114  			return "", err
   115  		}
   116  	}
   117  	if err := scanner.Err(); err != nil {
   118  		return "", err
   119  	}
   120  	return root, nil
   121  }
   122  
   123  func callern(n int) string {
   124  	_, file, line, ok := runtime.Caller(n)
   125  	if !ok {
   126  		return "<unknown>"
   127  	}
   128  	return filepath.Base(file) + ":" + strconv.Itoa(line)
   129  }
   130  
   131  func caller() string {
   132  	return callern(3)
   133  }
   134  
   135  type WCase struct {
   136  	Action func()
   137  	Events []EventInfo
   138  }
   139  
   140  func (cas WCase) String() string {
   141  	s := make([]string, 0, len(cas.Events))
   142  	for _, ei := range cas.Events {
   143  		s = append(s, "Event("+ei.Event().String()+")@"+filepath.FromSlash(ei.Path()))
   144  	}
   145  	return strings.Join(s, ", ")
   146  }
   147  
   148  type W struct {
   149  	Watcher watcher
   150  	C       chan EventInfo
   151  	Timeout time.Duration
   152  
   153  	t    *testing.T
   154  	root string
   155  }
   156  
   157  func newWatcherTest(t *testing.T, tree string) *W {
   158  	root, err := tmptree("", filepath.FromSlash(tree))
   159  	if err != nil {
   160  		t.Fatalf(`tmptree("", %q)=%v`, tree, err)
   161  	}
   162  	root, _, err = cleanpath(root)
   163  	if err != nil {
   164  		t.Fatalf(`cleanpath(%q)=%v`, root, err)
   165  	}
   166  	Sync()
   167  	return &W{
   168  		t:    t,
   169  		root: root,
   170  	}
   171  }
   172  
   173  func NewWatcherTest(t *testing.T, tree string, events ...Event) *W {
   174  	w := newWatcherTest(t, tree)
   175  	if len(events) == 0 {
   176  		events = []Event{Create, Remove, Write, Rename}
   177  	}
   178  	if rw, ok := w.watcher().(recursiveWatcher); ok {
   179  		if err := rw.RecursiveWatch(w.root, joinevents(events)); err != nil {
   180  			t.Fatalf("RecursiveWatch(%q, All)=%v", w.root, err)
   181  		}
   182  	} else {
   183  		fn := func(path string, fi os.FileInfo, err error) error {
   184  			if err != nil {
   185  				return err
   186  			}
   187  			if fi.IsDir() {
   188  				if err := w.watcher().Watch(path, joinevents(events)); err != nil {
   189  					return err
   190  				}
   191  			}
   192  			return nil
   193  		}
   194  		if err := filepath.Walk(w.root, fn); err != nil {
   195  			t.Fatalf("Walk(%q, fn)=%v", w.root, err)
   196  		}
   197  	}
   198  	drainall(w.C)
   199  	return w
   200  }
   201  
   202  func (w *W) clean(path string) string {
   203  	path, isrec, err := cleanpath(filepath.Join(w.root, path))
   204  	if err != nil {
   205  		w.Fatalf("cleanpath(%q)=%v", path, err)
   206  	}
   207  	if isrec {
   208  		path = path + "..."
   209  	}
   210  	return path
   211  }
   212  
   213  func (w *W) Fatal(v interface{}) {
   214  	w.t.Fatalf("%s: %v", caller(), v)
   215  }
   216  
   217  func (w *W) Fatalf(format string, v ...interface{}) {
   218  	w.t.Fatalf("%s: %s", caller(), fmt.Sprintf(format, v...))
   219  }
   220  
   221  func (w *W) Watch(path string, e Event) {
   222  	if err := w.watcher().Watch(w.clean(path), e); err != nil {
   223  		w.Fatalf("Watch(%s, %v)=%v", path, e, err)
   224  	}
   225  }
   226  
   227  func (w *W) Rewatch(path string, olde, newe Event) {
   228  	if err := w.watcher().Rewatch(w.clean(path), olde, newe); err != nil {
   229  		w.Fatalf("Rewatch(%s, %v, %v)=%v", path, olde, newe, err)
   230  	}
   231  }
   232  
   233  func (w *W) Unwatch(path string) {
   234  	if err := w.watcher().Unwatch(w.clean(path)); err != nil {
   235  		w.Fatalf("Unwatch(%s)=%v", path, err)
   236  	}
   237  }
   238  
   239  func (w *W) RecursiveWatch(path string, e Event) {
   240  	rw, ok := w.watcher().(recursiveWatcher)
   241  	if !ok {
   242  		w.Fatal("watcher does not implement recursive watching on this platform")
   243  	}
   244  	if err := rw.RecursiveWatch(w.clean(path), e); err != nil {
   245  		w.Fatalf("RecursiveWatch(%s, %v)=%v", path, e, err)
   246  	}
   247  }
   248  
   249  func (w *W) RecursiveRewatch(oldp, newp string, olde, newe Event) {
   250  	rw, ok := w.watcher().(recursiveWatcher)
   251  	if !ok {
   252  		w.Fatal("watcher does not implement recursive watching on this platform")
   253  	}
   254  	if err := rw.RecursiveRewatch(w.clean(oldp), w.clean(newp), olde, newe); err != nil {
   255  		w.Fatalf("RecursiveRewatch(%s, %s, %v, %v)=%v", oldp, newp, olde, newe, err)
   256  	}
   257  }
   258  
   259  func (w *W) RecursiveUnwatch(path string) {
   260  	rw, ok := w.watcher().(recursiveWatcher)
   261  	if !ok {
   262  		w.Fatal("watcher does not implement recursive watching on this platform")
   263  	}
   264  	if err := rw.RecursiveUnwatch(w.clean(path)); err != nil {
   265  		w.Fatalf("RecursiveUnwatch(%s)=%v", path, err)
   266  	}
   267  }
   268  
   269  func (w *W) initwatcher(buffer int) {
   270  	c := make(chan EventInfo, buffer)
   271  	w.Watcher = newWatcher(c)
   272  	w.C = c
   273  }
   274  
   275  func (w *W) watcher() watcher {
   276  	if w.Watcher == nil {
   277  		w.initwatcher(512)
   278  	}
   279  	return w.Watcher
   280  }
   281  
   282  func (w *W) c() chan EventInfo {
   283  	if w.C == nil {
   284  		w.initwatcher(512)
   285  	}
   286  	return w.C
   287  }
   288  
   289  func (w *W) timeout() time.Duration {
   290  	if w.Timeout != 0 {
   291  		return w.Timeout
   292  	}
   293  	return timeout()
   294  }
   295  
   296  func (w *W) Close() error {
   297  	defer os.RemoveAll(w.root)
   298  	if err := w.watcher().Close(); err != nil {
   299  		w.Fatalf("w.Watcher.Close()=%v", err)
   300  	}
   301  	return nil
   302  }
   303  
   304  func EqualEventInfo(want, got EventInfo) error {
   305  	if got.Event() != want.Event() {
   306  		return fmt.Errorf("want Event()=%v; got %v (path=%s)", want.Event(),
   307  			got.Event(), want.Path())
   308  	}
   309  	path := strings.TrimRight(filepath.FromSlash(want.Path()), `/\`)
   310  	if !strings.HasSuffix(got.Path(), path) {
   311  		return fmt.Errorf("want Path()=%s; got %s (event=%v)", path, got.Path(),
   312  			want.Event())
   313  	}
   314  	return nil
   315  }
   316  
   317  func HasEventInfo(want, got Event, p string) error {
   318  	if got&want != want {
   319  		return fmt.Errorf("want Event=%v; got %v (path=%s)", want,
   320  			got, p)
   321  	}
   322  	return nil
   323  }
   324  
   325  func EqualCall(want, got Call) error {
   326  	if want.F != got.F {
   327  		return fmt.Errorf("want F=%v; got %v (want.P=%q, got.P=%q)", want.F, got.F, want.P, got.P)
   328  	}
   329  	if got.E != want.E {
   330  		return fmt.Errorf("want E=%v; got %v (want.P=%q, got.P=%q)", want.E, got.E, want.P, got.P)
   331  	}
   332  	if got.NE != want.NE {
   333  		return fmt.Errorf("want NE=%v; got %v (want.P=%q, got.P=%q)", want.NE, got.NE, want.P, got.P)
   334  	}
   335  	if want.C != got.C {
   336  		return fmt.Errorf("want C=%p; got %p (want.P=%q, got.P=%q)", want.C, got.C, want.P, got.P)
   337  	}
   338  	if want := filepath.FromSlash(want.P); !strings.HasSuffix(got.P, want) {
   339  		return fmt.Errorf("want P=%s; got %s", want, got.P)
   340  	}
   341  	if want := filepath.FromSlash(want.NP); !strings.HasSuffix(got.NP, want) {
   342  		return fmt.Errorf("want NP=%s; got %s", want, got.NP)
   343  	}
   344  	return nil
   345  }
   346  
   347  func create(w *W, path string) WCase {
   348  	return WCase{
   349  		Action: func() {
   350  			isdir, err := tmpcreate(w.root, filepath.FromSlash(path))
   351  			if err != nil {
   352  				w.Fatalf("tmpcreate(%q, %q)=%v", w.root, path, err)
   353  			}
   354  			if isdir {
   355  				dbgprintf("[FS] os.Mkdir(%q)\n", path)
   356  			} else {
   357  				dbgprintf("[FS] os.Create(%q)\n", path)
   358  			}
   359  		},
   360  		Events: []EventInfo{
   361  			&Call{P: path, E: Create},
   362  		},
   363  	}
   364  }
   365  
   366  func remove(w *W, path string) WCase {
   367  	return WCase{
   368  		Action: func() {
   369  			if err := os.RemoveAll(filepath.Join(w.root, filepath.FromSlash(path))); err != nil {
   370  				w.Fatal(err)
   371  			}
   372  			dbgprintf("[FS] os.Remove(%q)\n", path)
   373  		},
   374  		Events: []EventInfo{
   375  			&Call{P: path, E: Remove},
   376  		},
   377  	}
   378  }
   379  
   380  func rename(w *W, oldpath, newpath string) WCase {
   381  	return WCase{
   382  		Action: func() {
   383  			err := os.Rename(filepath.Join(w.root, filepath.FromSlash(oldpath)),
   384  				filepath.Join(w.root, filepath.FromSlash(newpath)))
   385  			if err != nil {
   386  				w.Fatal(err)
   387  			}
   388  			dbgprintf("[FS] os.Rename(%q, %q)\n", oldpath, newpath)
   389  		},
   390  		Events: []EventInfo{
   391  			&Call{P: newpath, E: Rename},
   392  		},
   393  	}
   394  }
   395  
   396  func write(w *W, path string, p []byte) WCase {
   397  	return WCase{
   398  		Action: func() {
   399  			f, err := os.OpenFile(filepath.Join(w.root, filepath.FromSlash(path)),
   400  				os.O_WRONLY, 0644)
   401  			if err != nil {
   402  				w.Fatalf("OpenFile(%q)=%v", path, err)
   403  			}
   404  			if _, err := f.Write(p); err != nil {
   405  				w.Fatalf("Write(%q)=%v", path, err)
   406  			}
   407  			if err := nonil(f.Sync(), f.Close()); err != nil {
   408  				w.Fatalf("Sync(%q)/Close(%q)=%v", path, path, err)
   409  			}
   410  			dbgprintf("[FS] Write(%q)\n", path)
   411  		},
   412  		Events: []EventInfo{
   413  			&Call{P: path, E: Write},
   414  		},
   415  	}
   416  }
   417  
   418  func drainall(c chan EventInfo) (ei []EventInfo) {
   419  	time.Sleep(50 * time.Millisecond)
   420  	for {
   421  		select {
   422  		case e := <-c:
   423  			ei = append(ei, e)
   424  			runtime.Gosched()
   425  		default:
   426  			return
   427  		}
   428  	}
   429  }
   430  
   431  type WCaseFunc func(i int, cas WCase, ei EventInfo) error
   432  
   433  func (w *W) ExpectAnyFunc(cases []WCase, fn WCaseFunc) {
   434  	UpdateWait() // Wait some time before starting the test.
   435  Test:
   436  	for i, cas := range cases {
   437  		dbgprintf("ExpectAny: i=%d\n", i)
   438  		cas.Action()
   439  		Sync()
   440  		switch cas.Events {
   441  		case nil:
   442  			if ei := drainall(w.C); len(ei) != 0 {
   443  				w.Fatalf("unexpected dangling events: %v (i=%d)", ei, i)
   444  			}
   445  		default:
   446  			select {
   447  			case ei := <-w.C:
   448  				dbgprintf("received: path=%q, event=%v, sys=%v (i=%d)", ei.Path(),
   449  					ei.Event(), ei.Sys(), i)
   450  				for j, want := range cas.Events {
   451  					if err := EqualEventInfo(want, ei); err != nil {
   452  						dbgprint(err, j)
   453  						continue
   454  					}
   455  					if fn != nil {
   456  						if err := fn(i, cas, ei); err != nil {
   457  							w.Fatalf("ExpectAnyFunc(%d, %v)=%v", i, ei, err)
   458  						}
   459  					}
   460  					drainall(w.C) // TODO(rjeczalik): revisit
   461  					continue Test
   462  				}
   463  				w.Fatalf("ExpectAny received an event which does not match any of "+
   464  					"the expected ones (i=%d): want one of %v; got %v", i, cas.Events, ei)
   465  			case <-time.After(w.timeout()):
   466  				w.Fatalf("timed out after %v waiting for one of %v (i=%d)", w.timeout(),
   467  					cas.Events, i)
   468  			}
   469  			drainall(w.C) // TODO(rjeczalik): revisit
   470  		}
   471  	}
   472  }
   473  
   474  func (w *W) ExpectAny(cases []WCase) {
   475  	w.ExpectAnyFunc(cases, nil)
   476  }
   477  
   478  func (w *W) aggregate(ei []EventInfo, pf string) (evs map[string]Event) {
   479  	evs = make(map[string]Event)
   480  	for _, cas := range ei {
   481  		p := cas.Path()
   482  		if pf != "" {
   483  			p = filepath.Join(pf, p)
   484  		}
   485  		evs[p] |= cas.Event()
   486  	}
   487  	return
   488  }
   489  
   490  func (w *W) ExpectAllFunc(cases []WCase) {
   491  	UpdateWait() // Wait some time before starting the test.
   492  	for i, cas := range cases {
   493  		exp := w.aggregate(cas.Events, w.root)
   494  		dbgprintf("ExpectAll: i=%d\n", i)
   495  		cas.Action()
   496  		Sync()
   497  		got := w.aggregate(drainall(w.C), "")
   498  		for ep, ee := range exp {
   499  			ge, ok := got[ep]
   500  			if !ok {
   501  				w.Fatalf("missing events for %q (%v)", ep, ee)
   502  				continue
   503  			}
   504  			delete(got, ep)
   505  			if err := HasEventInfo(ee, ge, ep); err != nil {
   506  				w.Fatalf("ExpectAll received an event which does not match "+
   507  					"the expected ones for %q: want %v; got %v", ep, ee, ge)
   508  				continue
   509  			}
   510  		}
   511  		if len(got) != 0 {
   512  			w.Fatalf("ExpectAll received unexpected events: %v", got)
   513  		}
   514  	}
   515  }
   516  
   517  // ExpectAll requires all requested events to be send.
   518  // It does not require events to be send in the same order or in the same
   519  // chunks (e.g. NoteWrite and NoteExtend reported as independent events are
   520  // treated the same as one NoteWrite|NoteExtend event).
   521  func (w *W) ExpectAll(cases []WCase) {
   522  	w.ExpectAllFunc(cases)
   523  }
   524  
   525  // FuncType represents enums for Watcher interface.
   526  type FuncType string
   527  
   528  const (
   529  	FuncWatch            = FuncType("Watch")
   530  	FuncUnwatch          = FuncType("Unwatch")
   531  	FuncRewatch          = FuncType("Rewatch")
   532  	FuncRecursiveWatch   = FuncType("RecursiveWatch")
   533  	FuncRecursiveUnwatch = FuncType("RecursiveUnwatch")
   534  	FuncRecursiveRewatch = FuncType("RecursiveRewatch")
   535  	FuncStop             = FuncType("Stop")
   536  )
   537  
   538  type Chans []chan EventInfo
   539  
   540  func NewChans(n int) Chans {
   541  	ch := make([]chan EventInfo, n)
   542  	for i := range ch {
   543  		ch[i] = make(chan EventInfo, buffer)
   544  	}
   545  	return ch
   546  }
   547  
   548  func (c Chans) Foreach(fn func(chan<- EventInfo, node)) {
   549  	for i, ch := range c {
   550  		fn(ch, node{Name: strconv.Itoa(i)})
   551  	}
   552  }
   553  
   554  func (c Chans) Drain() (ei []EventInfo) {
   555  	n := len(c)
   556  	stop := make(chan struct{})
   557  	eich := make(chan EventInfo, n*buffer)
   558  	go func() {
   559  		defer close(eich)
   560  		cases := make([]reflect.SelectCase, n+1)
   561  		for i := range c {
   562  			cases[i].Chan = reflect.ValueOf(c[i])
   563  			cases[i].Dir = reflect.SelectRecv
   564  		}
   565  		cases[n].Chan = reflect.ValueOf(stop)
   566  		cases[n].Dir = reflect.SelectRecv
   567  		for {
   568  			i, v, ok := reflect.Select(cases)
   569  			if i == n {
   570  				return
   571  			}
   572  			if !ok {
   573  				panic("(Chans).Drain(): unexpected chan close")
   574  			}
   575  			eich <- v.Interface().(EventInfo)
   576  		}
   577  	}()
   578  	<-time.After(50 * time.Duration(n) * time.Millisecond)
   579  	close(stop)
   580  	for e := range eich {
   581  		ei = append(ei, e)
   582  	}
   583  	return
   584  }
   585  
   586  // Call represents single call to Watcher issued by the Tree
   587  // and recorded by a spy Watcher mock.
   588  type Call struct {
   589  	F   FuncType       // denotes type of function to call, for both watcher and notifier interface
   590  	C   chan EventInfo // user channel being an argument to either Watch or Stop function
   591  	P   string         // regular Path argument and old path from RecursiveRewatch call
   592  	NP  string         // new Path argument from RecursiveRewatch call
   593  	E   Event          // regular Event argument and old Event from a Rewatch call
   594  	NE  Event          // new Event argument from Rewatch call
   595  	S   interface{}    // when Call is used as EventInfo, S is a value of Sys()
   596  	Dir bool           // when Call is used as EventInfo, Dir is a value of isDir()
   597  }
   598  
   599  // Call implements the EventInfo interface.
   600  func (c *Call) Event() Event         { return c.E }
   601  func (c *Call) Path() string         { return c.P }
   602  func (c *Call) String() string       { return fmt.Sprintf("%#v", c) }
   603  func (c *Call) Sys() interface{}     { return c.S }
   604  func (c *Call) isDir() (bool, error) { return c.Dir, nil }
   605  
   606  // CallSlice is a convenient wrapper for a slice of Call values, which allows
   607  // to sort them in ascending order.
   608  type CallSlice []Call
   609  
   610  // CallSlice implements sort.Interface inteface.
   611  func (cs CallSlice) Len() int           { return len(cs) }
   612  func (cs CallSlice) Less(i, j int) bool { return cs[i].P < cs[j].P }
   613  func (cs CallSlice) Swap(i, j int)      { cs[i], cs[j] = cs[j], cs[i] }
   614  func (cs CallSlice) Sort()              { sort.Sort(cs) }
   615  
   616  // Spy is a mock for Watcher interface, which records every call.
   617  type Spy []Call
   618  
   619  func (s *Spy) Close() (_ error) { return }
   620  
   621  func (s *Spy) Watch(p string, e Event) (_ error) {
   622  	dbgprintf("%s: (*Spy).Watch(%q, %v)", caller(), p, e)
   623  	*s = append(*s, Call{F: FuncWatch, P: p, E: e})
   624  	return
   625  }
   626  
   627  func (s *Spy) Unwatch(p string) (_ error) {
   628  	dbgprintf("%s: (*Spy).Unwatch(%q)", caller(), p)
   629  	*s = append(*s, Call{F: FuncUnwatch, P: p})
   630  	return
   631  }
   632  
   633  func (s *Spy) Rewatch(p string, olde, newe Event) (_ error) {
   634  	dbgprintf("%s: (*Spy).Rewatch(%q, %v, %v)", caller(), p, olde, newe)
   635  	*s = append(*s, Call{F: FuncRewatch, P: p, E: olde, NE: newe})
   636  	return
   637  }
   638  
   639  func (s *Spy) RecursiveWatch(p string, e Event) (_ error) {
   640  	dbgprintf("%s: (*Spy).RecursiveWatch(%q, %v)", caller(), p, e)
   641  	*s = append(*s, Call{F: FuncRecursiveWatch, P: p, E: e})
   642  	return
   643  }
   644  
   645  func (s *Spy) RecursiveUnwatch(p string) (_ error) {
   646  	dbgprintf("%s: (*Spy).RecursiveUnwatch(%q)", caller(), p)
   647  	*s = append(*s, Call{F: FuncRecursiveUnwatch, P: p})
   648  	return
   649  }
   650  
   651  func (s *Spy) RecursiveRewatch(oldp, newp string, olde, newe Event) (_ error) {
   652  	dbgprintf("%s: (*Spy).RecursiveRewatch(%q, %q, %v, %v)", caller(), oldp, newp, olde, newe)
   653  	*s = append(*s, Call{F: FuncRecursiveRewatch, P: oldp, NP: newp, E: olde, NE: newe})
   654  	return
   655  }
   656  
   657  type RCase struct {
   658  	Call   Call
   659  	Record []Call
   660  }
   661  
   662  type TCase struct {
   663  	Event    Call
   664  	Receiver Chans
   665  }
   666  
   667  type NCase struct {
   668  	Event    WCase
   669  	Receiver Chans
   670  }
   671  
   672  type N struct {
   673  	Timeout time.Duration
   674  
   675  	t    *testing.T
   676  	tree tree
   677  	w    *W
   678  	spy  *Spy
   679  	c    chan EventInfo
   680  	j    int // spy offset
   681  
   682  	realroot string
   683  }
   684  
   685  func newN(t *testing.T, tree string) *N {
   686  	n := &N{
   687  		t: t,
   688  		w: newWatcherTest(t, tree),
   689  	}
   690  	realroot, err := canonical(n.w.root)
   691  	if err != nil {
   692  		t.Fatalf("%s: unexpected fixture failure: %v", caller(), err)
   693  	}
   694  	n.realroot = realroot
   695  	return n
   696  }
   697  
   698  func newTreeN(t *testing.T, tree string) *N {
   699  	c := make(chan EventInfo, buffer)
   700  	n := newN(t, tree)
   701  	n.spy = &Spy{}
   702  	n.w.Watcher = n.spy
   703  	n.w.C = c
   704  	n.c = c
   705  	return n
   706  }
   707  
   708  func NewNotifyTest(t *testing.T, tree string) *N {
   709  	n := newN(t, tree)
   710  	if rw, ok := n.w.watcher().(recursiveWatcher); ok {
   711  		n.tree = newRecursiveTree(rw, n.w.c())
   712  	} else {
   713  		n.tree = newNonrecursiveTree(n.w.watcher(), n.w.c(), nil)
   714  	}
   715  	t.Cleanup(n.Close)
   716  	return n
   717  }
   718  
   719  func NewRecursiveTreeTest(t *testing.T, tree string) *N {
   720  	n := newTreeN(t, tree)
   721  	n.tree = newRecursiveTree(n.spy, n.c)
   722  	t.Cleanup(n.Close)
   723  	return n
   724  }
   725  
   726  func NewNonrecursiveTreeTest(t *testing.T, tree string) *N {
   727  	n := newTreeN(t, tree)
   728  	n.tree = newNonrecursiveTree(n.spy, n.c, nil)
   729  	t.Cleanup(n.Close)
   730  	return n
   731  }
   732  
   733  func NewNonrecursiveTreeTestC(t *testing.T, tree string) (*N, chan EventInfo) {
   734  	rec := make(chan EventInfo, buffer)
   735  	recinternal := make(chan EventInfo, buffer)
   736  	recuser := make(chan EventInfo, buffer)
   737  	go func() {
   738  		for ei := range rec {
   739  			select {
   740  			case recinternal <- ei:
   741  			default:
   742  				t.Fatalf("failed to send ei to recinternal: not ready")
   743  			}
   744  			select {
   745  			case recuser <- ei:
   746  			default:
   747  				t.Fatalf("failed to send ei to recuser: not ready")
   748  			}
   749  		}
   750  	}()
   751  	n := newTreeN(t, tree)
   752  	tr := newNonrecursiveTree(n.spy, n.c, recinternal)
   753  	tr.rec = rec
   754  	n.tree = tr
   755  	t.Cleanup(n.Close)
   756  	return n, recuser
   757  }
   758  
   759  func (n *N) timeout() time.Duration {
   760  	if n.Timeout != 0 {
   761  		return n.Timeout
   762  	}
   763  	return n.w.timeout()
   764  }
   765  
   766  func (n *N) W() *W {
   767  	return n.w
   768  }
   769  
   770  func (n *N) Close() {
   771  	err := n.tree.Close()
   772  	os.RemoveAll(n.w.root)
   773  	if err != nil {
   774  		n.w.Fatalf("(notifier).Close()=%v", err)
   775  	}
   776  }
   777  
   778  func dummyDoNotWatch(path string) bool {
   779  	return false
   780  }
   781  
   782  func (n *N) Watch(path string, c chan<- EventInfo, events ...Event) {
   783  	UpdateWait() // we need to wait on Windows because of its asynchronous watcher.
   784  	path = filepath.Join(n.w.root, path)
   785  	if err := n.tree.Watch(path, c, dummyDoNotWatch, events...); err != nil {
   786  		n.t.Errorf("Watch(%s, %p, %v)=%v", path, c, events, err)
   787  	}
   788  }
   789  
   790  func (n *N) WatchErr(path string, c chan<- EventInfo, err error, events ...Event) {
   791  	path = filepath.Join(n.w.root, path)
   792  	switch e := n.tree.Watch(path, c, dummyDoNotWatch, events...); {
   793  	case err == nil && e == nil:
   794  		n.t.Errorf("Watch(%s, %p, %v)=nil", path, c, events)
   795  	case err != nil && e != err:
   796  		n.t.Errorf("Watch(%s, %p, %v)=%v != %v", path, c, events, e, err)
   797  	}
   798  }
   799  
   800  func (n *N) Stop(c chan<- EventInfo) {
   801  	n.tree.Stop(c)
   802  }
   803  
   804  func (n *N) Call(calls ...Call) {
   805  	for i := range calls {
   806  		switch calls[i].F {
   807  		case FuncWatch:
   808  			n.Watch(calls[i].P, calls[i].C, calls[i].E)
   809  		case FuncStop:
   810  			n.Stop(calls[i].C)
   811  		default:
   812  			panic("unsupported call type: " + string(calls[i].F))
   813  		}
   814  	}
   815  }
   816  
   817  func (n *N) expectDry(ch Chans, i int) {
   818  	if ei := ch.Drain(); len(ei) != 0 {
   819  		n.w.Fatalf("unexpected dangling events: %v (i=%d)", ei, i)
   820  	}
   821  }
   822  
   823  func (n *N) ExpectRecordedCalls(cases []RCase) {
   824  	for i, cas := range cases {
   825  		dbgprintf("ExpectRecordedCalls: i=%d\n", i)
   826  		n.Call(cas.Call)
   827  		record := (*n.spy)[n.j:]
   828  		if len(cas.Record) == 0 && len(record) == 0 {
   829  			continue
   830  		}
   831  		n.j = len(*n.spy)
   832  		if len(record) != len(cas.Record) {
   833  			n.t.Fatalf("%s: want len(record)=%d; got %d [%+v] (i=%d)", caller(),
   834  				len(cas.Record), len(record), record, i)
   835  		}
   836  		CallSlice(record).Sort()
   837  		for j := range cas.Record {
   838  			if err := EqualCall(cas.Record[j], record[j]); err != nil {
   839  				n.t.Fatalf("%s: %v (i=%d, j=%d)", caller(), err, i, j)
   840  			}
   841  		}
   842  	}
   843  }
   844  
   845  func (n *N) collect(ch Chans) <-chan []EventInfo {
   846  	done := make(chan []EventInfo)
   847  	go func() {
   848  		cases := make([]reflect.SelectCase, len(ch))
   849  		unique := make(map[<-chan EventInfo]EventInfo, len(ch))
   850  		for i := range ch {
   851  			cases[i].Chan = reflect.ValueOf(ch[i])
   852  			cases[i].Dir = reflect.SelectRecv
   853  		}
   854  		for i := len(cases); i != 0; i = len(cases) {
   855  			j, v, ok := reflect.Select(cases)
   856  			if !ok {
   857  				n.t.Fatal("unexpected chan close")
   858  			}
   859  			ch := cases[j].Chan.Interface().(chan EventInfo)
   860  			got := v.Interface().(EventInfo)
   861  			if ei, ok := unique[ch]; ok {
   862  				n.t.Fatalf("duplicated event %v (previous=%v) received on collect", got, ei)
   863  			}
   864  			unique[ch] = got
   865  			cases[j], cases = cases[i-1], cases[:i-1]
   866  		}
   867  		collected := make([]EventInfo, 0, len(ch))
   868  		for _, ch := range unique {
   869  			collected = append(collected, ch)
   870  		}
   871  		done <- collected
   872  	}()
   873  	return done
   874  }
   875  
   876  func (n *N) abs(rel Call) *Call {
   877  	rel.P = filepath.Join(n.realroot, filepath.FromSlash(rel.P))
   878  	if !filepath.IsAbs(rel.P) {
   879  		rel.P = filepath.Join(wd, rel.P)
   880  	}
   881  	return &rel
   882  }
   883  
   884  func (n *N) ExpectTreeEvents(cases []TCase, all Chans) {
   885  	for i, cas := range cases {
   886  		dbgprintf("ExpectTreeEvents: i=%d\n", i)
   887  		// Ensure there're no dangling event left by previous test-case.
   888  		n.expectDry(all, i)
   889  		n.c <- n.abs(cas.Event)
   890  		switch cas.Receiver {
   891  		case nil:
   892  			n.expectDry(all, i)
   893  		default:
   894  			ch := n.collect(cas.Receiver)
   895  			select {
   896  			case collected := <-ch:
   897  				for _, got := range collected {
   898  					if err := EqualEventInfo(&cas.Event, got); err != nil {
   899  						n.w.Fatalf("%s: %s (i=%d)", caller(), err, i)
   900  					}
   901  				}
   902  			case <-time.After(n.timeout()):
   903  				n.w.Fatalf("ExpectTreeEvents has timed out after %v waiting for"+
   904  					" %v on %s (i=%d)", n.timeout(), cas.Event.E, cas.Event.P, i)
   905  			}
   906  
   907  		}
   908  	}
   909  	n.expectDry(all, -1)
   910  }
   911  
   912  func (n *N) ExpectNotifyEvents(cases []NCase, all Chans) {
   913  	UpdateWait() // Wait some time before starting the test.
   914  	for i, cas := range cases {
   915  		dbgprintf("ExpectNotifyEvents: i=%d\n", i)
   916  		cas.Event.Action()
   917  		Sync()
   918  		switch cas.Receiver {
   919  		case nil:
   920  			n.expectDry(all, i)
   921  		default:
   922  			ch := n.collect(cas.Receiver)
   923  			select {
   924  			case collected := <-ch:
   925  			Compare:
   926  				for j, ei := range collected {
   927  					dbgprintf("received: path=%q, event=%v, sys=%v (i=%d, j=%d)", ei.Path(),
   928  						ei.Event(), ei.Sys(), i, j)
   929  					for _, want := range cas.Event.Events {
   930  						if err := EqualEventInfo(want, ei); err != nil {
   931  							dbgprint(err, j)
   932  							continue
   933  						}
   934  						continue Compare
   935  					}
   936  					n.w.Fatalf("ExpectNotifyEvents received an event which does not"+
   937  						" match any of the expected ones (i=%d): want one of %v; got %v", i,
   938  						cas.Event.Events, ei)
   939  				}
   940  			case <-time.After(n.timeout()):
   941  				n.w.Fatalf("ExpectNotifyEvents did not receive any of the expected events [%v] "+
   942  					"after %v (i=%d)", cas.Event, n.timeout(), i)
   943  			}
   944  		}
   945  	}
   946  	n.expectDry(all, -1)
   947  }
   948  
   949  func (n *N) Walk(fn walkFunc) {
   950  	switch t := n.tree.(type) {
   951  	case *recursiveTree:
   952  		if err := t.root.Walk("", fn, nil); err != nil {
   953  			n.w.Fatal(err)
   954  		}
   955  	case *nonrecursiveTree:
   956  		if err := t.root.Walk("", fn, nil); err != nil {
   957  			n.w.Fatal(err)
   958  		}
   959  	default:
   960  		n.t.Fatal("unknown tree type")
   961  	}
   962  }