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