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