1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
11
jc "github.com/juju/testing/checkers"
12
gc "gopkg.in/check.v1"
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"
22
type stringsWorkerSuite struct {
28
var _ = gc.Suite(&stringsWorkerSuite{})
30
func newStringsHandlerWorker(c *gc.C, setupError, handlerError, teardownError error) (*stringsHandler, worker.Worker) {
31
sh := &stringsHandler{
33
handled: make(chan []string, 1),
34
setupError: setupError,
35
teardownError: teardownError,
36
handlerError: handlerError,
37
watcher: &testStringsWatcher{
38
changes: make(chan []string),
40
setupDone: make(chan struct{}),
42
w := legacy.NewStringsWorker(sh)
45
case <-time.After(coretesting.ShortWait):
46
c.Error("Failed waiting for stringsHandler.Setup to be called during SetUpTest")
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)
57
type stringsHandler struct {
60
// Signal handled when we get a handle() call
65
watcher *testStringsWatcher
66
setupDone chan struct{}
69
func (sh *stringsHandler) SetUp() (state.StringsWatcher, error) {
70
defer func() { sh.setupDone <- struct{}{} }()
73
sh.actions = append(sh.actions, "setup")
74
if sh.watcher == nil {
75
return nil, sh.setupError
77
return sh.watcher, sh.setupError
80
func (sh *stringsHandler) TearDown() error {
83
sh.actions = append(sh.actions, "teardown")
84
if sh.handled != nil {
87
return sh.teardownError
90
func (sh *stringsHandler) Handle(changes []string) error {
93
sh.actions = append(sh.actions, "handler")
94
if sh.handled != nil {
95
// Unlock while we are waiting for the send
100
return sh.handlerError
103
func (sh *stringsHandler) CheckActions(c *gc.C, actions ...string) {
106
c.Check(sh.actions, gc.DeepEquals, actions)
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) {
115
done := make(chan error)
117
done <- worker.Stop(s.worker)
119
err := waitForTimeout(c, done, coretesting.LongWait)
120
c.Check(err, jc.ErrorIsNil)
125
type testStringsWatcher struct {
128
changes chan []string
133
func (tsw *testStringsWatcher) Changes() <-chan []string {
137
func (tsw *testStringsWatcher) Err() error {
141
func (tsw *testStringsWatcher) Stop() error {
143
defer tsw.mu.Unlock()
151
func (tsw *testStringsWatcher) SetStopError(err error) {
157
func (tsw *testStringsWatcher) TriggerChange(c *gc.C, changes []string) {
159
case tsw.changes <- changes:
160
case <-time.After(coretesting.LongWait):
161
c.Errorf("timed out trying to trigger a change")
165
func waitForHandledStrings(c *gc.C, handled chan []string, expect []string) {
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)
174
func (s *stringsWorkerSuite) TestKill(c *gc.C) {
176
err := waitShort(c, s.worker)
177
c.Assert(err, jc.ErrorIsNil)
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)
188
func (s *stringsWorkerSuite) TestWait(c *gc.C) {
189
done := make(chan error)
191
done <- s.worker.Wait()
193
// Wait should not return until we've killed the worker
196
c.Errorf("Wait() didn't wait until we stopped it: %v", err)
197
case <-time.After(coretesting.ShortWait):
200
err := waitForTimeout(c, done, coretesting.LongWait)
201
c.Assert(err, jc.ErrorIsNil)
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
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)
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")
229
func (s *stringsWorkerSuite) TestSetUpFailureStopsWithTearDown(c *gc.C) {
230
// Stop the worker and SetUp again, this time with an error
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)
240
func (s *stringsWorkerSuite) TestWatcherStopFailurePropagates(c *gc.C) {
241
s.actor.watcher.SetStopError(fmt.Errorf("error while stopping watcher"))
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
249
func (s *stringsWorkerSuite) TestCleanRunNoticesTearDownError(c *gc.C) {
250
s.actor.teardownError = fmt.Errorf("failed to tear down watcher")
252
c.Assert(s.worker.Wait(), gc.ErrorMatches, "failed to tear down watcher")
256
func (s *stringsWorkerSuite) TestHandleErrorStopsWorkerAndWatcher(c *gc.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)
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
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
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()
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
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()
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")