github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/uniter/resolver/loop_test.go (about)

     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  }