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