1
// Copyright 2013-2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
10
"github.com/juju/errors"
11
jc "github.com/juju/testing/checkers"
12
gc "gopkg.in/check.v1"
15
coretesting "github.com/juju/juju/testing"
16
"github.com/juju/juju/watcher"
17
"github.com/juju/juju/worker"
20
type stringsWorkerSuite struct {
26
var _ = gc.Suite(&stringsWorkerSuite{})
28
func newStringsHandlerWorker(c *gc.C, setupError, handlerError, teardownError error) (*stringsHandler, worker.Worker) {
29
sh := &stringsHandler{
31
handled: make(chan []string, 1),
32
setupError: setupError,
33
teardownError: teardownError,
34
handlerError: handlerError,
35
watcher: newTestStringsWatcher(),
36
setupDone: make(chan struct{}),
38
w, err := watcher.NewStringsWorker(watcher.StringsConfig{Handler: sh})
39
c.Assert(err, jc.ErrorIsNil)
42
case <-time.After(coretesting.ShortWait):
43
c.Error("Failed waiting for stringsHandler.Setup to be called during SetUpTest")
48
func (s *stringsWorkerSuite) SetUpTest(c *gc.C) {
49
s.BaseSuite.SetUpTest(c)
50
s.actor, s.worker = newStringsHandlerWorker(c, nil, nil, nil)
51
s.AddCleanup(s.stopWorker)
54
type stringsHandler struct {
57
// Signal handled when we get a handle() call
62
watcher *testStringsWatcher
63
setupDone chan struct{}
66
func (sh *stringsHandler) SetUp() (watcher.StringsWatcher, error) {
67
defer func() { sh.setupDone <- struct{}{} }()
70
sh.actions = append(sh.actions, "setup")
71
if sh.watcher == nil {
72
return nil, sh.setupError
74
return sh.watcher, sh.setupError
77
func (sh *stringsHandler) TearDown() error {
80
sh.actions = append(sh.actions, "teardown")
81
if sh.handled != nil {
84
return sh.teardownError
87
func (sh *stringsHandler) Handle(_ <-chan struct{}, changes []string) error {
90
sh.actions = append(sh.actions, "handler")
91
if sh.handled != nil {
92
// Unlock while we are waiting for the send
97
return sh.handlerError
100
func (sh *stringsHandler) CheckActions(c *gc.C, actions ...string) {
103
c.Check(sh.actions, gc.DeepEquals, actions)
106
// During teardown we try to stop the worker, but don't hang the test suite if
107
// Stop never returns
108
func (s *stringsWorkerSuite) stopWorker(c *gc.C) {
112
done := make(chan error)
114
done <- worker.Stop(s.worker)
116
err := waitForTimeout(c, done, coretesting.LongWait)
117
c.Check(err, jc.ErrorIsNil)
122
func newTestStringsWatcher() *testStringsWatcher {
123
w := &testStringsWatcher{
124
changes: make(chan []string),
133
type testStringsWatcher struct {
135
changes chan []string
140
func (tsw *testStringsWatcher) Changes() watcher.StringsChannel {
144
func (tsw *testStringsWatcher) Kill() {
146
tsw.tomb.Kill(tsw.stopError)
150
func (tsw *testStringsWatcher) Wait() error {
151
return tsw.tomb.Wait()
154
func (tsw *testStringsWatcher) Stopped() bool {
156
case <-tsw.tomb.Dead():
163
func (tsw *testStringsWatcher) SetStopError(err error) {
169
func (tsw *testStringsWatcher) TriggerChange(c *gc.C, changes []string) {
171
case tsw.changes <- changes:
172
case <-time.After(coretesting.LongWait):
173
c.Errorf("timed out trying to trigger a change")
177
func waitForHandledStrings(c *gc.C, handled chan []string, expect []string) {
179
case changes := <-handled:
180
c.Assert(changes, gc.DeepEquals, expect)
181
case <-time.After(coretesting.LongWait):
182
c.Errorf("handled failed to signal after %s", coretesting.LongWait)
186
func (s *stringsWorkerSuite) TestKill(c *gc.C) {
188
err := waitShort(c, s.worker)
189
c.Assert(err, jc.ErrorIsNil)
192
func (s *stringsWorkerSuite) TestStop(c *gc.C) {
193
err := worker.Stop(s.worker)
194
c.Assert(err, jc.ErrorIsNil)
195
// After stop, Wait should return right away
196
err = waitShort(c, s.worker)
197
c.Assert(err, jc.ErrorIsNil)
200
func (s *stringsWorkerSuite) TestWait(c *gc.C) {
201
done := make(chan error)
203
done <- s.worker.Wait()
205
// Wait should not return until we've killed the worker
208
c.Errorf("Wait() didn't wait until we stopped it: %v", err)
209
case <-time.After(coretesting.ShortWait):
212
err := waitForTimeout(c, done, coretesting.LongWait)
213
c.Assert(err, jc.ErrorIsNil)
216
func (s *stringsWorkerSuite) TestCallSetUpAndTearDown(c *gc.C) {
217
// After calling NewStringsWorker, we should have called setup
218
s.actor.CheckActions(c, "setup")
219
// If we kill the worker, it should notice, and call teardown
221
err := waitShort(c, s.worker)
222
c.Check(err, jc.ErrorIsNil)
223
s.actor.CheckActions(c, "setup", "teardown")
224
c.Check(s.actor.watcher.Stopped(), jc.IsTrue)
227
func (s *stringsWorkerSuite) TestChangesTriggerHandler(c *gc.C) {
228
s.actor.CheckActions(c, "setup")
229
s.actor.watcher.TriggerChange(c, []string{"aa", "bb"})
230
waitForHandledStrings(c, s.actor.handled, []string{"aa", "bb"})
231
s.actor.CheckActions(c, "setup", "handler")
232
s.actor.watcher.TriggerChange(c, []string{"cc", "dd"})
233
waitForHandledStrings(c, s.actor.handled, []string{"cc", "dd"})
234
s.actor.watcher.TriggerChange(c, []string{"ee", "ff"})
235
waitForHandledStrings(c, s.actor.handled, []string{"ee", "ff"})
236
s.actor.CheckActions(c, "setup", "handler", "handler", "handler")
237
c.Assert(worker.Stop(s.worker), gc.IsNil)
238
s.actor.CheckActions(c, "setup", "handler", "handler", "handler", "teardown")
241
func (s *stringsWorkerSuite) TestSetUpFailureStopsWithTearDown(c *gc.C) {
242
// Stop the worker and SetUp again, this time with an error
244
actor, w := newStringsHandlerWorker(c, errors.New("my special error"), nil, nil)
245
err := waitShort(c, w)
246
c.Check(err, gc.ErrorMatches, "my special error")
247
actor.CheckActions(c, "setup", "teardown")
248
c.Check(actor.watcher.Stopped(), jc.IsTrue)
251
func (s *stringsWorkerSuite) TestWatcherStopFailurePropagates(c *gc.C) {
252
s.actor.watcher.SetStopError(errors.New("error while stopping watcher"))
254
c.Assert(s.worker.Wait(), gc.ErrorMatches, "error while stopping watcher")
255
// We've already stopped the worker, don't let teardown notice the
256
// worker is in an error state
260
func (s *stringsWorkerSuite) TestCleanRunNoticesTearDownError(c *gc.C) {
261
s.actor.teardownError = errors.New("failed to tear down watcher")
263
c.Assert(s.worker.Wait(), gc.ErrorMatches, "failed to tear down watcher")
267
func (s *stringsWorkerSuite) TestHandleErrorStopsWorkerAndWatcher(c *gc.C) {
269
actor, w := newStringsHandlerWorker(c, nil, errors.New("my handling error"), nil)
270
actor.watcher.TriggerChange(c, []string{"aa", "bb"})
271
waitForHandledStrings(c, actor.handled, []string{"aa", "bb"})
272
err := waitShort(c, w)
273
c.Check(err, gc.ErrorMatches, "my handling error")
274
actor.CheckActions(c, "setup", "handler", "teardown")
275
c.Check(actor.watcher.Stopped(), jc.IsTrue)
278
func (s *stringsWorkerSuite) TestNoticesStoppedWatcher(c *gc.C) {
279
// The default closedHandler doesn't panic if you have a genuine error
280
// (because it assumes you want to propagate a real error and then
282
s.actor.watcher.SetStopError(errors.New("Stopped Watcher"))
283
s.actor.watcher.Kill()
284
err := waitShort(c, s.worker)
285
c.Check(err, gc.ErrorMatches, "Stopped Watcher")
286
s.actor.CheckActions(c, "setup", "teardown")
287
// Worker is stopped, don't fail TearDownTest
291
func (s *stringsWorkerSuite) TestErrorsOnClosedChannel(c *gc.C) {
292
close(s.actor.watcher.changes)
293
err := waitShort(c, s.worker)
294
c.Check(err, gc.ErrorMatches, "change channel closed")
295
s.actor.CheckActions(c, "setup", "teardown")