github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/presence/pinger_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package presence_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names"
    11  	"github.com/juju/testing"
    12  	jc "github.com/juju/testing/checkers"
    13  	"github.com/juju/utils/clock"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/apiserver/presence"
    17  	coretesting "github.com/juju/juju/testing"
    18  	"github.com/juju/juju/worker/workertest"
    19  )
    20  
    21  type WorkerSuite struct {
    22  	testing.IsolationSuite
    23  
    24  	stub   *testing.Stub
    25  	pinger *stubPinger
    26  	clock  *stubClock
    27  	cfg    presence.Config
    28  }
    29  
    30  var _ = gc.Suite(&WorkerSuite{})
    31  
    32  func (s *WorkerSuite) SetUpTest(c *gc.C) {
    33  	s.IsolationSuite.SetUpTest(c)
    34  
    35  	s.stub = &testing.Stub{}
    36  	s.pinger = &stubPinger{Stub: s.stub}
    37  	s.clock = &stubClock{StubClock: coretesting.NewStubClock(s.stub)}
    38  	s.cfg = presence.Config{
    39  		Identity:   names.NewMachineTag("1"),
    40  		Start:      s.start,
    41  		Clock:      s.clock,
    42  		RetryDelay: time.Nanosecond,
    43  	}
    44  }
    45  
    46  func (s *WorkerSuite) start() (presence.Pinger, error) {
    47  	s.stub.AddCall("start")
    48  	if err := s.stub.NextErr(); err != nil {
    49  		return nil, errors.Trace(err)
    50  	}
    51  	return s.pinger, nil
    52  }
    53  
    54  func (s *WorkerSuite) TestConfigValidateOkay(c *gc.C) {
    55  	cfg := presence.Config{
    56  		Identity:   names.NewMachineTag("1"),
    57  		Start:      func() (presence.Pinger, error) { return nil, nil },
    58  		Clock:      struct{ clock.Clock }{},
    59  		RetryDelay: time.Second,
    60  	}
    61  
    62  	err := cfg.Validate()
    63  
    64  	c.Check(err, jc.ErrorIsNil)
    65  }
    66  
    67  func (s *WorkerSuite) TestConfigValidateNothingUsed(c *gc.C) {
    68  	cfg := presence.Config{
    69  		Identity:   names.NewMachineTag("1"),
    70  		Start:      s.start,
    71  		Clock:      coretesting.NewStubClock(s.stub),
    72  		RetryDelay: time.Second,
    73  	}
    74  
    75  	err := cfg.Validate()
    76  	c.Assert(err, jc.ErrorIsNil)
    77  
    78  	s.stub.CheckNoCalls(c)
    79  }
    80  
    81  func (s *WorkerSuite) TestConfigValidateZeroValue(c *gc.C) {
    82  	var cfg presence.Config
    83  
    84  	err := cfg.Validate()
    85  
    86  	c.Check(err, gc.NotNil)
    87  }
    88  
    89  func (s *WorkerSuite) TestConfigValidateMissingIdentity(c *gc.C) {
    90  	cfg := presence.Config{
    91  		Start:      func() (presence.Pinger, error) { return nil, nil },
    92  		Clock:      struct{ clock.Clock }{},
    93  		RetryDelay: time.Second,
    94  	}
    95  
    96  	err := cfg.Validate()
    97  
    98  	c.Check(err, jc.Satisfies, errors.IsNotValid)
    99  	c.Check(err, gc.ErrorMatches, `nil Identity not valid`)
   100  }
   101  
   102  func (s *WorkerSuite) TestConfigValidateMissingStart(c *gc.C) {
   103  	cfg := presence.Config{
   104  		Identity:   names.NewMachineTag("1"),
   105  		Clock:      struct{ clock.Clock }{},
   106  		RetryDelay: time.Second,
   107  	}
   108  
   109  	err := cfg.Validate()
   110  
   111  	c.Check(err, jc.Satisfies, errors.IsNotValid)
   112  	c.Check(err, gc.ErrorMatches, `nil Start not valid`)
   113  }
   114  
   115  func (s *WorkerSuite) TestConfigValidateMissingClock(c *gc.C) {
   116  	cfg := presence.Config{
   117  		Identity:   names.NewMachineTag("1"),
   118  		Start:      func() (presence.Pinger, error) { return nil, nil },
   119  		RetryDelay: time.Second,
   120  	}
   121  
   122  	err := cfg.Validate()
   123  
   124  	c.Check(err, jc.Satisfies, errors.IsNotValid)
   125  	c.Check(err, gc.ErrorMatches, `nil Clock not valid`)
   126  }
   127  
   128  func (s *WorkerSuite) TestConfigValidateMissingRetryDelay(c *gc.C) {
   129  	cfg := presence.Config{
   130  		Identity: names.NewMachineTag("1"),
   131  		Start:    func() (presence.Pinger, error) { return nil, nil },
   132  		Clock:    struct{ clock.Clock }{},
   133  	}
   134  
   135  	err := cfg.Validate()
   136  
   137  	c.Check(err, jc.Satisfies, errors.IsNotValid)
   138  	c.Check(err, gc.ErrorMatches, `non-positive RetryDelay not valid`)
   139  }
   140  
   141  func (s *WorkerSuite) TestNewRunOnceBeforeLoop(c *gc.C) {
   142  	waitChan := make(chan struct{})
   143  	s.clock.notify = waitChan
   144  
   145  	w, err := presence.New(s.cfg)
   146  	c.Assert(err, jc.ErrorIsNil)
   147  	defer workertest.CleanKill(c, w)
   148  	<-waitChan
   149  
   150  	s.stub.CheckCallNames(c,
   151  		"start",
   152  		"Wait",
   153  		"After",
   154  	)
   155  }
   156  
   157  func (s *WorkerSuite) TestNewFailStart(c *gc.C) {
   158  	waitChan := make(chan struct{})
   159  	s.clock.notify = waitChan
   160  	failure := errors.New("<failure>")
   161  	s.stub.SetErrors(failure)
   162  
   163  	w, err := presence.New(s.cfg)
   164  	c.Assert(err, jc.ErrorIsNil)
   165  	defer workertest.CleanKill(c, w)
   166  	<-waitChan
   167  
   168  	s.stub.CheckCallNames(c,
   169  		"start",
   170  		"After", // continued on
   171  	)
   172  }
   173  
   174  func (s *WorkerSuite) TestNewFailWait(c *gc.C) {
   175  	waitChan := make(chan struct{})
   176  	s.clock.notify = waitChan
   177  	failure := errors.New("<failure>")
   178  	s.stub.SetErrors(nil, failure)
   179  
   180  	w, err := presence.New(s.cfg)
   181  	c.Assert(err, jc.ErrorIsNil)
   182  	defer workertest.CleanKill(c, w)
   183  	<-waitChan
   184  
   185  	s.stub.CheckCallNames(c,
   186  		"start",
   187  		"Wait",
   188  		"After", // continued on
   189  	)
   190  }
   191  
   192  func (s *WorkerSuite) TestNewLoop(c *gc.C) {
   193  	waitChan := make(chan struct{})
   194  	block := make(chan struct{})
   195  	s.clock.setAfter(4)
   196  	count := 0
   197  	s.cfg.Start = func() (presence.Pinger, error) {
   198  		pinger, err := s.start()
   199  		c.Logf("%d", count)
   200  		if count > 3 {
   201  			s.pinger.notify = waitChan
   202  			s.pinger.waitBlock = block
   203  		}
   204  		count += 1
   205  		return pinger, err
   206  	}
   207  
   208  	w, err := presence.New(s.cfg)
   209  	c.Assert(err, jc.ErrorIsNil)
   210  	defer workertest.CleanKill(c, w)
   211  	defer close(block)
   212  	<-waitChan
   213  
   214  	s.stub.CheckCallNames(c,
   215  		"start", "Wait", "After",
   216  		"start", "Wait", "After",
   217  		"start", "Wait", "After",
   218  		"start", "Wait", "After",
   219  		"start", "Wait",
   220  	)
   221  }
   222  
   223  func (s *WorkerSuite) TestNewRetry(c *gc.C) {
   224  	failure := errors.New("<failure>")
   225  	s.stub.SetErrors(
   226  		nil, nil, nil,
   227  		failure, nil,
   228  		failure, nil,
   229  		nil, failure, nil,
   230  		nil, nil,
   231  		failure, // never reached
   232  	)
   233  	waitChan := make(chan struct{})
   234  	block := make(chan struct{})
   235  	s.clock.setAfter(5)
   236  	delay := time.Nanosecond
   237  	s.cfg.RetryDelay = delay
   238  	count := 0
   239  	s.cfg.Start = func() (presence.Pinger, error) {
   240  		pinger, err := s.start()
   241  		if count > 4 {
   242  			s.pinger.notify = waitChan
   243  			s.pinger.waitBlock = block
   244  		}
   245  		count += 1
   246  		return pinger, err
   247  	}
   248  
   249  	w, err := presence.New(s.cfg)
   250  	c.Assert(err, jc.ErrorIsNil)
   251  	defer workertest.CleanKill(c, w)
   252  	defer close(block)
   253  	<-waitChan
   254  
   255  	s.stub.CheckCallNames(c,
   256  		"start", "Wait", "After",
   257  		"start", "After",
   258  		"start", "After",
   259  		"start", "Wait", "After",
   260  		"start", "Wait", "After",
   261  		"start", "Wait",
   262  	)
   263  	var noWait time.Duration
   264  	s.stub.CheckCall(c, 2, "After", noWait)
   265  	s.stub.CheckCall(c, 4, "After", delay)
   266  	s.stub.CheckCall(c, 6, "After", delay)
   267  	s.stub.CheckCall(c, 9, "After", noWait)
   268  	s.stub.CheckCall(c, 12, "After", noWait)
   269  }
   270  
   271  func (s *WorkerSuite) TestNewInvalidConfig(c *gc.C) {
   272  	var cfg presence.Config
   273  
   274  	_, err := presence.New(cfg)
   275  
   276  	c.Check(err, jc.Satisfies, errors.IsNotValid)
   277  }
   278  
   279  type stubPinger struct {
   280  	*testing.Stub
   281  
   282  	waitBlock chan struct{}
   283  	notify    chan struct{}
   284  }
   285  
   286  func (s *stubPinger) Stop() error {
   287  	s.AddCall("Stop")
   288  	if err := s.NextErr(); err != nil {
   289  		return errors.Trace(err)
   290  	}
   291  	return nil
   292  }
   293  
   294  func (s *stubPinger) Wait() error {
   295  	s.AddCall("Wait")
   296  	if err := s.NextErr(); err != nil {
   297  		return errors.Trace(err)
   298  	}
   299  
   300  	if s.notify != nil {
   301  		s.notify <- struct{}{}
   302  	}
   303  	if s.waitBlock != nil {
   304  		<-s.waitBlock
   305  	}
   306  	return nil
   307  }
   308  
   309  type stubClock struct {
   310  	*coretesting.StubClock
   311  
   312  	notify chan struct{}
   313  }
   314  
   315  func (s *stubClock) setAfter(numCalls int) {
   316  	after := make(chan time.Time, numCalls)
   317  	for i := 0; i < numCalls; i++ {
   318  		after <- time.Now()
   319  	}
   320  	s.ReturnAfter = after
   321  }
   322  
   323  func (s *stubClock) After(d time.Duration) <-chan time.Time {
   324  	after := s.StubClock.After(d)
   325  	if s.notify != nil {
   326  		s.notify <- struct{}{}
   327  	}
   328  	return after
   329  }