~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/worker/uniter/resolver/loop_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 2015 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package resolver_test
 
5
 
 
6
import (
 
7
        "errors"
 
8
        "time"
 
9
 
 
10
        jc "github.com/juju/testing/checkers"
 
11
        gc "gopkg.in/check.v1"
 
12
        "gopkg.in/juju/charm.v6-unstable"
 
13
 
 
14
        "github.com/juju/juju/testing"
 
15
        coretesting "github.com/juju/juju/testing"
 
16
        "github.com/juju/juju/worker/uniter/operation"
 
17
        "github.com/juju/juju/worker/uniter/remotestate"
 
18
        "github.com/juju/juju/worker/uniter/resolver"
 
19
)
 
20
 
 
21
type LoopSuite struct {
 
22
        testing.BaseSuite
 
23
 
 
24
        resolver  resolver.Resolver
 
25
        watcher   *mockRemoteStateWatcher
 
26
        opFactory *mockOpFactory
 
27
        executor  *mockOpExecutor
 
28
        charmURL  *charm.URL
 
29
        abort     chan struct{}
 
30
        onIdle    func() error
 
31
}
 
32
 
 
33
var _ = gc.Suite(&LoopSuite{})
 
34
 
 
35
func (s *LoopSuite) SetUpTest(c *gc.C) {
 
36
        s.BaseSuite.SetUpTest(c)
 
37
        s.resolver = resolver.ResolverFunc(func(resolver.LocalState, remotestate.Snapshot, operation.Factory) (operation.Operation, error) {
 
38
                return nil, resolver.ErrNoOperation
 
39
        })
 
40
        s.watcher = &mockRemoteStateWatcher{
 
41
                changes: make(chan struct{}, 1),
 
42
        }
 
43
        s.opFactory = &mockOpFactory{}
 
44
        s.executor = &mockOpExecutor{}
 
45
        s.charmURL = charm.MustParseURL("cs:trusty/mysql")
 
46
        s.abort = make(chan struct{})
 
47
}
 
48
 
 
49
func (s *LoopSuite) loop() (resolver.LocalState, error) {
 
50
        localState := resolver.LocalState{
 
51
                CharmURL: s.charmURL,
 
52
        }
 
53
        err := resolver.Loop(resolver.LoopConfig{
 
54
                Resolver:      s.resolver,
 
55
                Factory:       s.opFactory,
 
56
                Watcher:       s.watcher,
 
57
                Executor:      s.executor,
 
58
                Abort:         s.abort,
 
59
                OnIdle:        s.onIdle,
 
60
                CharmDirGuard: &mockCharmDirGuard{},
 
61
        }, &localState)
 
62
        return localState, err
 
63
}
 
64
 
 
65
func (s *LoopSuite) TestAbort(c *gc.C) {
 
66
        close(s.abort)
 
67
        _, err := s.loop()
 
68
        c.Assert(err, gc.Equals, resolver.ErrLoopAborted)
 
69
}
 
70
 
 
71
func (s *LoopSuite) TestOnIdle(c *gc.C) {
 
72
        onIdleCh := make(chan interface{}, 1)
 
73
        s.onIdle = func() error {
 
74
                onIdleCh <- nil
 
75
                return nil
 
76
        }
 
77
 
 
78
        done := make(chan interface{}, 1)
 
79
        go func() {
 
80
                _, err := s.loop()
 
81
                done <- err
 
82
        }()
 
83
 
 
84
        waitChannel(c, onIdleCh, "waiting for onIdle")
 
85
        s.watcher.changes <- struct{}{}
 
86
        waitChannel(c, onIdleCh, "waiting for onIdle")
 
87
        close(s.abort)
 
88
 
 
89
        err := waitChannel(c, done, "waiting for loop to exit")
 
90
        c.Assert(err, gc.Equals, resolver.ErrLoopAborted)
 
91
 
 
92
        select {
 
93
        case <-onIdleCh:
 
94
                c.Fatal("unexpected onIdle call")
 
95
        default:
 
96
        }
 
97
}
 
98
 
 
99
func (s *LoopSuite) TestOnIdleError(c *gc.C) {
 
100
        s.onIdle = func() error {
 
101
                return errors.New("onIdle failed")
 
102
        }
 
103
        close(s.abort)
 
104
        _, err := s.loop()
 
105
        c.Assert(err, gc.ErrorMatches, "onIdle failed")
 
106
}
 
107
 
 
108
func (s *LoopSuite) TestErrWaitingNoOnIdle(c *gc.C) {
 
109
        var onIdleCalled bool
 
110
        s.onIdle = func() error {
 
111
                onIdleCalled = true
 
112
                return nil
 
113
        }
 
114
        s.resolver = resolver.ResolverFunc(func(
 
115
                _ resolver.LocalState,
 
116
                _ remotestate.Snapshot,
 
117
                _ operation.Factory,
 
118
        ) (operation.Operation, error) {
 
119
                return nil, resolver.ErrWaiting
 
120
        })
 
121
        close(s.abort)
 
122
        _, err := s.loop()
 
123
        c.Assert(err, gc.Equals, resolver.ErrLoopAborted)
 
124
        c.Assert(onIdleCalled, jc.IsFalse)
 
125
}
 
126
 
 
127
func (s *LoopSuite) TestInitialFinalLocalState(c *gc.C) {
 
128
        var local resolver.LocalState
 
129
        s.resolver = resolver.ResolverFunc(func(
 
130
                l resolver.LocalState,
 
131
                _ remotestate.Snapshot,
 
132
                _ operation.Factory,
 
133
        ) (operation.Operation, error) {
 
134
                local = l
 
135
                return nil, resolver.ErrNoOperation
 
136
        })
 
137
 
 
138
        close(s.abort)
 
139
        lastLocal, err := s.loop()
 
140
        c.Assert(err, gc.Equals, resolver.ErrLoopAborted)
 
141
        c.Assert(local, jc.DeepEquals, resolver.LocalState{
 
142
                CharmURL: s.charmURL,
 
143
        })
 
144
        c.Assert(lastLocal, jc.DeepEquals, local)
 
145
}
 
146
 
 
147
func (s *LoopSuite) TestLoop(c *gc.C) {
 
148
        var resolverCalls int
 
149
        theOp := &mockOp{}
 
150
        s.resolver = resolver.ResolverFunc(func(
 
151
                _ resolver.LocalState,
 
152
                _ remotestate.Snapshot,
 
153
                _ operation.Factory,
 
154
        ) (operation.Operation, error) {
 
155
                resolverCalls++
 
156
                switch resolverCalls {
 
157
                // On the first call, return an operation.
 
158
                case 1:
 
159
                        return theOp, nil
 
160
                // On the second call, simulate having
 
161
                // no operations to perform, at which
 
162
                // point we'll wait for a remote state
 
163
                // change.
 
164
                case 2:
 
165
                        s.watcher.changes <- struct{}{}
 
166
                        break
 
167
                // On the third call, kill the loop.
 
168
                case 3:
 
169
                        close(s.abort)
 
170
                        break
 
171
                }
 
172
                return nil, resolver.ErrNoOperation
 
173
        })
 
174
 
 
175
        _, err := s.loop()
 
176
        c.Assert(err, gc.Equals, resolver.ErrLoopAborted)
 
177
        c.Assert(resolverCalls, gc.Equals, 3)
 
178
        s.executor.CheckCallNames(c, "State", "State", "Run", "State", "State")
 
179
        c.Assert(s.executor.Calls()[2].Args, jc.SameContents, []interface{}{theOp})
 
180
}
 
181
 
 
182
func (s *LoopSuite) TestRunFails(c *gc.C) {
 
183
        s.executor.SetErrors(errors.New("Run fails"))
 
184
        s.resolver = resolver.ResolverFunc(func(
 
185
                _ resolver.LocalState,
 
186
                _ remotestate.Snapshot,
 
187
                _ operation.Factory,
 
188
        ) (operation.Operation, error) {
 
189
                return mockOp{}, nil
 
190
        })
 
191
        _, err := s.loop()
 
192
        c.Assert(err, gc.ErrorMatches, "Run fails")
 
193
}
 
194
 
 
195
func (s *LoopSuite) TestNextOpFails(c *gc.C) {
 
196
        s.resolver = resolver.ResolverFunc(func(
 
197
                _ resolver.LocalState,
 
198
                _ remotestate.Snapshot,
 
199
                _ operation.Factory,
 
200
        ) (operation.Operation, error) {
 
201
                return nil, errors.New("NextOp fails")
 
202
        })
 
203
        _, err := s.loop()
 
204
        c.Assert(err, gc.ErrorMatches, "NextOp fails")
 
205
}
 
206
 
 
207
func waitChannel(c *gc.C, ch <-chan interface{}, activity string) interface{} {
 
208
        select {
 
209
        case v := <-ch:
 
210
                return v
 
211
        case <-time.After(coretesting.LongWait):
 
212
                c.Fatalf("timed out " + activity)
 
213
                panic("unreachable")
 
214
        }
 
215
}