~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/watcher/legacy/stringsworker_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
}