1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
10
jc "github.com/juju/testing/checkers"
11
gc "gopkg.in/check.v1"
12
"gopkg.in/juju/charm.v6-unstable"
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"
21
type LoopSuite struct {
24
resolver resolver.Resolver
25
watcher *mockRemoteStateWatcher
26
opFactory *mockOpFactory
27
executor *mockOpExecutor
33
var _ = gc.Suite(&LoopSuite{})
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
40
s.watcher = &mockRemoteStateWatcher{
41
changes: make(chan struct{}, 1),
43
s.opFactory = &mockOpFactory{}
44
s.executor = &mockOpExecutor{}
45
s.charmURL = charm.MustParseURL("cs:trusty/mysql")
46
s.abort = make(chan struct{})
49
func (s *LoopSuite) loop() (resolver.LocalState, error) {
50
localState := resolver.LocalState{
53
err := resolver.Loop(resolver.LoopConfig{
60
CharmDirGuard: &mockCharmDirGuard{},
62
return localState, err
65
func (s *LoopSuite) TestAbort(c *gc.C) {
68
c.Assert(err, gc.Equals, resolver.ErrLoopAborted)
71
func (s *LoopSuite) TestOnIdle(c *gc.C) {
72
onIdleCh := make(chan interface{}, 1)
73
s.onIdle = func() error {
78
done := make(chan interface{}, 1)
84
waitChannel(c, onIdleCh, "waiting for onIdle")
85
s.watcher.changes <- struct{}{}
86
waitChannel(c, onIdleCh, "waiting for onIdle")
89
err := waitChannel(c, done, "waiting for loop to exit")
90
c.Assert(err, gc.Equals, resolver.ErrLoopAborted)
94
c.Fatal("unexpected onIdle call")
99
func (s *LoopSuite) TestOnIdleError(c *gc.C) {
100
s.onIdle = func() error {
101
return errors.New("onIdle failed")
105
c.Assert(err, gc.ErrorMatches, "onIdle failed")
108
func (s *LoopSuite) TestErrWaitingNoOnIdle(c *gc.C) {
109
var onIdleCalled bool
110
s.onIdle = func() error {
114
s.resolver = resolver.ResolverFunc(func(
115
_ resolver.LocalState,
116
_ remotestate.Snapshot,
118
) (operation.Operation, error) {
119
return nil, resolver.ErrWaiting
123
c.Assert(err, gc.Equals, resolver.ErrLoopAborted)
124
c.Assert(onIdleCalled, jc.IsFalse)
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,
133
) (operation.Operation, error) {
135
return nil, resolver.ErrNoOperation
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,
144
c.Assert(lastLocal, jc.DeepEquals, local)
147
func (s *LoopSuite) TestLoop(c *gc.C) {
148
var resolverCalls int
150
s.resolver = resolver.ResolverFunc(func(
151
_ resolver.LocalState,
152
_ remotestate.Snapshot,
154
) (operation.Operation, error) {
156
switch resolverCalls {
157
// On the first call, return an operation.
160
// On the second call, simulate having
161
// no operations to perform, at which
162
// point we'll wait for a remote state
165
s.watcher.changes <- struct{}{}
167
// On the third call, kill the loop.
172
return nil, resolver.ErrNoOperation
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})
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,
188
) (operation.Operation, error) {
192
c.Assert(err, gc.ErrorMatches, "Run fails")
195
func (s *LoopSuite) TestNextOpFails(c *gc.C) {
196
s.resolver = resolver.ResolverFunc(func(
197
_ resolver.LocalState,
198
_ remotestate.Snapshot,
200
) (operation.Operation, error) {
201
return nil, errors.New("NextOp fails")
204
c.Assert(err, gc.ErrorMatches, "NextOp fails")
207
func waitChannel(c *gc.C, ch <-chan interface{}, activity string) interface{} {
211
case <-time.After(coretesting.LongWait):
212
c.Fatalf("timed out " + activity)