github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/watcher/notify_test.go (about) 1 // Copyright 2012-2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package watcher_test 5 6 import ( 7 "sync" 8 "time" 9 10 "github.com/juju/errors" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 "gopkg.in/tomb.v1" 14 15 coretesting "github.com/juju/juju/testing" 16 "github.com/juju/juju/watcher" 17 "github.com/juju/juju/worker" 18 ) 19 20 type notifyWorkerSuite struct { 21 coretesting.BaseSuite 22 worker worker.Worker 23 actor *notifyHandler 24 } 25 26 var _ = gc.Suite(¬ifyWorkerSuite{}) 27 28 func newNotifyHandlerWorker(c *gc.C, setupError, handlerError, teardownError error) (*notifyHandler, worker.Worker) { 29 nh := ¬ifyHandler{ 30 actions: nil, 31 handled: make(chan struct{}, 1), 32 setupError: setupError, 33 teardownError: teardownError, 34 handlerError: handlerError, 35 watcher: newTestNotifyWatcher(), 36 setupDone: make(chan struct{}), 37 } 38 w, err := watcher.NewNotifyWorker(watcher.NotifyConfig{Handler: nh}) 39 c.Assert(err, jc.ErrorIsNil) 40 select { 41 case <-nh.setupDone: 42 case <-time.After(coretesting.ShortWait): 43 c.Error("Failed waiting for notifyHandler.Setup to be called during SetUpTest") 44 } 45 return nh, w 46 } 47 48 func (s *notifyWorkerSuite) SetUpTest(c *gc.C) { 49 s.BaseSuite.SetUpTest(c) 50 s.actor, s.worker = newNotifyHandlerWorker(c, nil, nil, nil) 51 s.AddCleanup(s.stopWorker) 52 } 53 54 type notifyHandler struct { 55 actions []string 56 mu sync.Mutex 57 // Signal handled when we get a handle() call 58 handled chan struct{} 59 setupError error 60 teardownError error 61 handlerError error 62 watcher *testNotifyWatcher 63 setupDone chan struct{} 64 } 65 66 func (nh *notifyHandler) SetUp() (watcher.NotifyWatcher, error) { 67 defer func() { nh.setupDone <- struct{}{} }() 68 nh.mu.Lock() 69 defer nh.mu.Unlock() 70 nh.actions = append(nh.actions, "setup") 71 if nh.watcher == nil { 72 return nil, nh.setupError 73 } 74 return nh.watcher, nh.setupError 75 } 76 77 func (nh *notifyHandler) TearDown() error { 78 nh.mu.Lock() 79 defer nh.mu.Unlock() 80 nh.actions = append(nh.actions, "teardown") 81 if nh.handled != nil { 82 close(nh.handled) 83 } 84 return nh.teardownError 85 } 86 87 func (nh *notifyHandler) Handle(_ <-chan struct{}) error { 88 nh.mu.Lock() 89 defer nh.mu.Unlock() 90 nh.actions = append(nh.actions, "handler") 91 if nh.handled != nil { 92 // Unlock while we are waiting for the send 93 nh.mu.Unlock() 94 nh.handled <- struct{}{} 95 nh.mu.Lock() 96 } 97 return nh.handlerError 98 } 99 100 func (nh *notifyHandler) CheckActions(c *gc.C, actions ...string) { 101 nh.mu.Lock() 102 defer nh.mu.Unlock() 103 c.Check(nh.actions, gc.DeepEquals, actions) 104 } 105 106 // During teardown we try to stop the worker, but don't hang the test suite if 107 // Stop never returns 108 func (s *notifyWorkerSuite) stopWorker(c *gc.C) { 109 if s.worker == nil { 110 return 111 } 112 done := make(chan error) 113 go func() { 114 done <- worker.Stop(s.worker) 115 }() 116 err := waitForTimeout(c, done, coretesting.LongWait) 117 c.Check(err, jc.ErrorIsNil) 118 s.actor = nil 119 s.worker = nil 120 } 121 122 func newTestNotifyWatcher() *testNotifyWatcher { 123 w := &testNotifyWatcher{ 124 changes: make(chan struct{}), 125 } 126 go func() { 127 defer w.tomb.Done() 128 <-w.tomb.Dying() 129 }() 130 return w 131 } 132 133 type testNotifyWatcher struct { 134 tomb tomb.Tomb 135 changes chan struct{} 136 mu sync.Mutex 137 stopError error 138 } 139 140 func (tnw *testNotifyWatcher) Changes() watcher.NotifyChannel { 141 return tnw.changes 142 } 143 144 func (tnw *testNotifyWatcher) Kill() { 145 tnw.mu.Lock() 146 tnw.tomb.Kill(tnw.stopError) 147 tnw.mu.Unlock() 148 } 149 150 func (tnw *testNotifyWatcher) Wait() error { 151 return tnw.tomb.Wait() 152 } 153 154 func (tnw *testNotifyWatcher) Stopped() bool { 155 select { 156 case <-tnw.tomb.Dead(): 157 return true 158 default: 159 return false 160 } 161 } 162 163 func (tnw *testNotifyWatcher) SetStopError(err error) { 164 tnw.mu.Lock() 165 tnw.stopError = err 166 tnw.mu.Unlock() 167 } 168 169 func (tnw *testNotifyWatcher) TriggerChange(c *gc.C) { 170 select { 171 case tnw.changes <- struct{}{}: 172 case <-time.After(coretesting.LongWait): 173 c.Errorf("timed out trying to trigger a change") 174 } 175 } 176 177 func waitForTimeout(c *gc.C, ch <-chan error, timeout time.Duration) error { 178 select { 179 case err := <-ch: 180 return err 181 case <-time.After(timeout): 182 c.Errorf("timed out waiting to receive a change after %s", timeout) 183 } 184 return nil 185 } 186 187 func waitShort(c *gc.C, w worker.Worker) error { 188 done := make(chan error) 189 go func() { 190 done <- w.Wait() 191 }() 192 return waitForTimeout(c, done, coretesting.ShortWait) 193 } 194 195 func waitForHandledNotify(c *gc.C, handled chan struct{}) { 196 select { 197 case <-handled: 198 case <-time.After(coretesting.LongWait): 199 c.Errorf("handled failed to signal after %s", coretesting.LongWait) 200 } 201 } 202 203 func (s *notifyWorkerSuite) TestKill(c *gc.C) { 204 s.worker.Kill() 205 err := waitShort(c, s.worker) 206 c.Assert(err, jc.ErrorIsNil) 207 } 208 209 func (s *notifyWorkerSuite) TestStop(c *gc.C) { 210 err := worker.Stop(s.worker) 211 c.Assert(err, jc.ErrorIsNil) 212 // After stop, Wait should return right away 213 err = waitShort(c, s.worker) 214 c.Assert(err, jc.ErrorIsNil) 215 } 216 217 func (s *notifyWorkerSuite) TestWait(c *gc.C) { 218 done := make(chan error) 219 go func() { 220 done <- s.worker.Wait() 221 }() 222 // Wait should not return until we've killed the worker 223 select { 224 case err := <-done: 225 c.Errorf("Wait() didn't wait until we stopped it: %v", err) 226 case <-time.After(coretesting.ShortWait): 227 } 228 s.worker.Kill() 229 err := waitForTimeout(c, done, coretesting.LongWait) 230 c.Assert(err, jc.ErrorIsNil) 231 } 232 233 func (s *notifyWorkerSuite) TestCallSetUpAndTearDown(c *gc.C) { 234 // After calling NewNotifyWorker, we should have called setup 235 s.actor.CheckActions(c, "setup") 236 // If we kill the worker, it should notice, and call teardown 237 s.worker.Kill() 238 err := waitShort(c, s.worker) 239 c.Check(err, jc.ErrorIsNil) 240 s.actor.CheckActions(c, "setup", "teardown") 241 c.Check(s.actor.watcher.Stopped(), jc.IsTrue) 242 } 243 244 func (s *notifyWorkerSuite) TestChangesTriggerHandler(c *gc.C) { 245 s.actor.CheckActions(c, "setup") 246 s.actor.watcher.TriggerChange(c) 247 waitForHandledNotify(c, s.actor.handled) 248 s.actor.CheckActions(c, "setup", "handler") 249 s.actor.watcher.TriggerChange(c) 250 waitForHandledNotify(c, s.actor.handled) 251 s.actor.watcher.TriggerChange(c) 252 waitForHandledNotify(c, s.actor.handled) 253 s.actor.CheckActions(c, "setup", "handler", "handler", "handler") 254 c.Assert(worker.Stop(s.worker), gc.IsNil) 255 s.actor.CheckActions(c, "setup", "handler", "handler", "handler", "teardown") 256 } 257 258 func (s *notifyWorkerSuite) TestSetUpFailureStopsWithTearDown(c *gc.C) { 259 // Stop the worker and SetUp again, this time with an error 260 s.stopWorker(c) 261 actor, w := newNotifyHandlerWorker(c, errors.New("my special error"), nil, errors.New("teardown")) 262 err := waitShort(c, w) 263 c.Check(err, gc.ErrorMatches, "my special error") 264 actor.CheckActions(c, "setup", "teardown") 265 c.Check(actor.watcher.Stopped(), jc.IsTrue) 266 } 267 268 func (s *notifyWorkerSuite) TestWatcherStopFailurePropagates(c *gc.C) { 269 s.actor.watcher.SetStopError(errors.New("error while stopping watcher")) 270 s.worker.Kill() 271 c.Assert(s.worker.Wait(), gc.ErrorMatches, "error while stopping watcher") 272 // We've already stopped the worker, don't let teardown notice the 273 // worker is in an error state 274 s.worker = nil 275 } 276 277 func (s *notifyWorkerSuite) TestCleanRunNoticesTearDownError(c *gc.C) { 278 s.actor.teardownError = errors.New("failed to tear down watcher") 279 s.worker.Kill() 280 c.Assert(s.worker.Wait(), gc.ErrorMatches, "failed to tear down watcher") 281 s.worker = nil 282 } 283 284 func (s *notifyWorkerSuite) TestHandleErrorStopsWorkerAndWatcher(c *gc.C) { 285 s.stopWorker(c) 286 actor, w := newNotifyHandlerWorker(c, nil, errors.New("my handling error"), nil) 287 actor.watcher.TriggerChange(c) 288 waitForHandledNotify(c, actor.handled) 289 err := waitShort(c, w) 290 c.Check(err, gc.ErrorMatches, "my handling error") 291 actor.CheckActions(c, "setup", "handler", "teardown") 292 c.Check(actor.watcher.Stopped(), jc.IsTrue) 293 } 294 295 func (s *notifyWorkerSuite) TestNoticesStoppedWatcher(c *gc.C) { 296 s.actor.watcher.SetStopError(errors.New("Stopped Watcher")) 297 s.actor.watcher.Kill() 298 err := waitShort(c, s.worker) 299 c.Check(err, gc.ErrorMatches, "Stopped Watcher") 300 s.actor.CheckActions(c, "setup", "teardown") 301 s.worker = nil 302 } 303 304 func (s *notifyWorkerSuite) TestErrorsOnClosedChannel(c *gc.C) { 305 close(s.actor.watcher.changes) 306 err := waitShort(c, s.worker) 307 c.Check(err, gc.ErrorMatches, "change channel closed") 308 s.actor.CheckActions(c, "setup", "teardown") 309 s.worker = nil 310 }