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