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