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