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