github.com/checksum/notify@v0.0.0-20190119234841-59aa2d88664f/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 dummyDoNotWatch(path string) bool { 776 return false 777 } 778 779 func (n *N) Watch(path string, c chan<- EventInfo, events ...Event) { 780 UpdateWait() // we need to wait on Windows because of its asynchronous watcher. 781 path = filepath.Join(n.w.root, path) 782 if err := n.tree.Watch(path, c, dummyDoNotWatch, events...); err != nil { 783 n.t.Errorf("Watch(%s, %p, %v)=%v", path, c, events, err) 784 } 785 } 786 787 func (n *N) WatchErr(path string, c chan<- EventInfo, err error, events ...Event) { 788 path = filepath.Join(n.w.root, path) 789 switch e := n.tree.Watch(path, c, dummyDoNotWatch, events...); { 790 case err == nil && e == nil: 791 n.t.Errorf("Watch(%s, %p, %v)=nil", path, c, events) 792 case err != nil && e != err: 793 n.t.Errorf("Watch(%s, %p, %v)=%v != %v", path, c, events, e, err) 794 } 795 } 796 797 func (n *N) Stop(c chan<- EventInfo) { 798 n.tree.Stop(c) 799 } 800 801 func (n *N) Call(calls ...Call) { 802 for i := range calls { 803 switch calls[i].F { 804 case FuncWatch: 805 n.Watch(calls[i].P, calls[i].C, calls[i].E) 806 case FuncStop: 807 n.Stop(calls[i].C) 808 default: 809 panic("unsupported call type: " + string(calls[i].F)) 810 } 811 } 812 } 813 814 func (n *N) expectDry(ch Chans, i int) { 815 if ei := ch.Drain(); len(ei) != 0 { 816 n.w.Fatalf("unexpected dangling events: %v (i=%d)", ei, i) 817 } 818 } 819 820 func (n *N) ExpectRecordedCalls(cases []RCase) { 821 for i, cas := range cases { 822 dbgprintf("ExpectRecordedCalls: i=%d\n", i) 823 n.Call(cas.Call) 824 record := (*n.spy)[n.j:] 825 if len(cas.Record) == 0 && len(record) == 0 { 826 continue 827 } 828 n.j = len(*n.spy) 829 if len(record) != len(cas.Record) { 830 n.t.Fatalf("%s: want len(record)=%d; got %d [%+v] (i=%d)", caller(), 831 len(cas.Record), len(record), record, i) 832 } 833 CallSlice(record).Sort() 834 for j := range cas.Record { 835 if err := EqualCall(cas.Record[j], record[j]); err != nil { 836 n.t.Fatalf("%s: %v (i=%d, j=%d)", caller(), err, i, j) 837 } 838 } 839 } 840 } 841 842 func (n *N) collect(ch Chans) <-chan []EventInfo { 843 done := make(chan []EventInfo) 844 go func() { 845 cases := make([]reflect.SelectCase, len(ch)) 846 unique := make(map[<-chan EventInfo]EventInfo, len(ch)) 847 for i := range ch { 848 cases[i].Chan = reflect.ValueOf(ch[i]) 849 cases[i].Dir = reflect.SelectRecv 850 } 851 for i := len(cases); i != 0; i = len(cases) { 852 j, v, ok := reflect.Select(cases) 853 if !ok { 854 n.t.Fatal("unexpected chan close") 855 } 856 ch := cases[j].Chan.Interface().(chan EventInfo) 857 got := v.Interface().(EventInfo) 858 if ei, ok := unique[ch]; ok { 859 n.t.Fatalf("duplicated event %v (previous=%v) received on collect", got, ei) 860 } 861 unique[ch] = got 862 cases[j], cases = cases[i-1], cases[:i-1] 863 } 864 collected := make([]EventInfo, 0, len(ch)) 865 for _, ch := range unique { 866 collected = append(collected, ch) 867 } 868 done <- collected 869 }() 870 return done 871 } 872 873 func (n *N) abs(rel Call) *Call { 874 rel.P = filepath.Join(n.realroot, filepath.FromSlash(rel.P)) 875 if !filepath.IsAbs(rel.P) { 876 rel.P = filepath.Join(wd, rel.P) 877 } 878 return &rel 879 } 880 881 func (n *N) ExpectTreeEvents(cases []TCase, all Chans) { 882 for i, cas := range cases { 883 dbgprintf("ExpectTreeEvents: i=%d\n", i) 884 // Ensure there're no dangling event left by previous test-case. 885 n.expectDry(all, i) 886 n.c <- n.abs(cas.Event) 887 switch cas.Receiver { 888 case nil: 889 n.expectDry(all, i) 890 default: 891 ch := n.collect(cas.Receiver) 892 select { 893 case collected := <-ch: 894 for _, got := range collected { 895 if err := EqualEventInfo(&cas.Event, got); err != nil { 896 n.w.Fatalf("%s: %s (i=%d)", caller(), err, i) 897 } 898 } 899 case <-time.After(n.timeout()): 900 n.w.Fatalf("ExpectTreeEvents has timed out after %v waiting for"+ 901 " %v on %s (i=%d)", n.timeout(), cas.Event.E, cas.Event.P, i) 902 } 903 904 } 905 } 906 n.expectDry(all, -1) 907 } 908 909 func (n *N) ExpectNotifyEvents(cases []NCase, all Chans) { 910 UpdateWait() // Wait some time before starting the test. 911 for i, cas := range cases { 912 dbgprintf("ExpectNotifyEvents: i=%d\n", i) 913 cas.Event.Action() 914 Sync() 915 switch cas.Receiver { 916 case nil: 917 n.expectDry(all, i) 918 default: 919 ch := n.collect(cas.Receiver) 920 select { 921 case collected := <-ch: 922 Compare: 923 for j, ei := range collected { 924 dbgprintf("received: path=%q, event=%v, sys=%v (i=%d, j=%d)", ei.Path(), 925 ei.Event(), ei.Sys(), i, j) 926 for _, want := range cas.Event.Events { 927 if err := EqualEventInfo(want, ei); err != nil { 928 dbgprint(err, j) 929 continue 930 } 931 continue Compare 932 } 933 n.w.Fatalf("ExpectNotifyEvents received an event which does not"+ 934 " match any of the expected ones (i=%d): want one of %v; got %v", i, 935 cas.Event.Events, ei) 936 } 937 case <-time.After(n.timeout()): 938 n.w.Fatalf("ExpectNotifyEvents did not receive any of the expected events [%v] "+ 939 "after %v (i=%d)", cas.Event, n.timeout(), i) 940 } 941 } 942 } 943 n.expectDry(all, -1) 944 } 945 946 func (n *N) Walk(fn walkFunc) { 947 switch t := n.tree.(type) { 948 case *recursiveTree: 949 if err := t.root.Walk("", fn); err != nil { 950 n.w.Fatal(err) 951 } 952 case *nonrecursiveTree: 953 if err := t.root.Walk("", fn); err != nil { 954 n.w.Fatal(err) 955 } 956 default: 957 n.t.Fatal("unknown tree type") 958 } 959 }