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