github.com/grahambrereton-form3/tilt@v0.10.18/internal/engine/fake_notify_test.go (about) 1 package engine 2 3 import ( 4 "path/filepath" 5 "sync" 6 7 "github.com/windmilleng/tilt/internal/ospath" 8 "github.com/windmilleng/tilt/internal/watch" 9 "github.com/windmilleng/tilt/pkg/logger" 10 ) 11 12 type fakeMultiWatcher struct { 13 events chan watch.FileEvent 14 errors chan error 15 16 mu sync.Mutex 17 watchers []*fakeWatcher 18 subs []chan watch.FileEvent 19 subsErrors []chan error 20 } 21 22 func newFakeMultiWatcher() *fakeMultiWatcher { 23 r := &fakeMultiWatcher{events: make(chan watch.FileEvent, 20), errors: make(chan error, 20)} 24 go r.loop() 25 return r 26 } 27 28 func (w *fakeMultiWatcher) newSub(paths []string, ignore watch.PathMatcher, _ logger.Logger) (watch.Notify, error) { 29 subCh := make(chan watch.FileEvent) 30 errorCh := make(chan error) 31 w.mu.Lock() 32 defer w.mu.Unlock() 33 34 watcher := newFakeWatcher(subCh, errorCh, paths, ignore) 35 w.watchers = append(w.watchers, watcher) 36 w.subs = append(w.subs, subCh) 37 w.subsErrors = append(w.subsErrors, errorCh) 38 return watcher, nil 39 } 40 41 func (w *fakeMultiWatcher) getSubs() []chan watch.FileEvent { 42 w.mu.Lock() 43 defer w.mu.Unlock() 44 return append([]chan watch.FileEvent{}, w.subs...) 45 } 46 47 func (w *fakeMultiWatcher) getSubErrors() []chan error { 48 w.mu.Lock() 49 defer w.mu.Unlock() 50 return append([]chan error{}, w.subsErrors...) 51 } 52 53 func (w *fakeMultiWatcher) loop() { 54 defer func() { 55 for _, sub := range w.getSubs() { 56 close(sub) 57 } 58 }() 59 60 defer func() { 61 for _, sub := range w.getSubErrors() { 62 close(sub) 63 } 64 }() 65 66 for { 67 select { 68 case e, ok := <-w.events: 69 if !ok { 70 return 71 } 72 for _, watcher := range w.watchers { 73 if watcher.matches(e.Path()) { 74 watcher.inboundCh <- e 75 } 76 } 77 case e, ok := <-w.errors: 78 if !ok { 79 return 80 } 81 for _, sub := range w.getSubErrors() { 82 sub <- e 83 } 84 } 85 } 86 } 87 88 type fakeWatcher struct { 89 inboundCh chan watch.FileEvent 90 outboundCh chan watch.FileEvent 91 errorCh chan error 92 93 paths []string 94 ignore watch.PathMatcher 95 } 96 97 func newFakeWatcher(inboundCh chan watch.FileEvent, errorCh chan error, paths []string, ignore watch.PathMatcher) *fakeWatcher { 98 for i, path := range paths { 99 paths[i], _ = filepath.Abs(path) 100 } 101 102 return &fakeWatcher{ 103 inboundCh: inboundCh, 104 outboundCh: make(chan watch.FileEvent, 20), 105 errorCh: errorCh, 106 paths: paths, 107 ignore: ignore, 108 } 109 } 110 111 func (w *fakeWatcher) matches(path string) bool { 112 ignore, _ := w.ignore.Matches(path) 113 if ignore { 114 return false 115 } 116 117 for _, watched := range w.paths { 118 if ospath.IsChild(watched, path) { 119 return true 120 } 121 } 122 return false 123 } 124 125 func (w *fakeWatcher) Start() error { 126 go w.loop() 127 return nil 128 } 129 130 func (w *fakeWatcher) Close() error { 131 return nil 132 } 133 134 func (w *fakeWatcher) Errors() chan error { 135 return w.errorCh 136 } 137 138 func (w *fakeWatcher) Events() chan watch.FileEvent { 139 return w.outboundCh 140 } 141 142 func (w *fakeWatcher) loop() { 143 var q []watch.FileEvent 144 for { 145 if len(q) == 0 { 146 select { 147 case e, ok := <-w.inboundCh: 148 if !ok { 149 close(w.outboundCh) 150 return 151 } 152 q = append(q, e) 153 } 154 } else { 155 e := q[0] 156 select { 157 case w.outboundCh <- e: 158 q = q[1:] 159 } 160 } 161 } 162 } 163 164 var _ watch.Notify = &fakeWatcher{}