github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/globalclockupdater/worker_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package globalclockupdater_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/clock/testclock"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/loggo"
    12  	"github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  	"gopkg.in/juju/worker.v1/workertest"
    16  
    17  	"github.com/juju/juju/core/globalclock"
    18  	coretesting "github.com/juju/juju/testing"
    19  	"github.com/juju/juju/worker/globalclockupdater"
    20  )
    21  
    22  type WorkerSuite struct {
    23  	testing.IsolationSuite
    24  	stub       testing.Stub
    25  	localClock *testclock.Clock
    26  	updater    stubUpdater
    27  	config     globalclockupdater.Config
    28  }
    29  
    30  var _ = gc.Suite(&WorkerSuite{})
    31  
    32  func (s *WorkerSuite) SetUpTest(c *gc.C) {
    33  	s.IsolationSuite.SetUpTest(c)
    34  	s.stub.ResetCalls()
    35  	s.localClock = testclock.NewClock(time.Time{})
    36  	s.updater = stubUpdater{
    37  		added: make(chan time.Duration, 1),
    38  	}
    39  	s.config = globalclockupdater.Config{
    40  		NewUpdater: func() (globalclock.Updater, error) {
    41  			s.stub.AddCall("NewUpdater")
    42  			return &s.updater, s.stub.NextErr()
    43  		},
    44  		LocalClock:     s.localClock,
    45  		UpdateInterval: time.Second,
    46  		BackoffDelay:   time.Minute,
    47  		Logger:         loggo.GetLogger("globalclockupdater_test"),
    48  	}
    49  }
    50  
    51  func (s *WorkerSuite) TestNewWorkerValidateNewUpdater(c *gc.C) {
    52  	s.config.NewUpdater = nil
    53  	s.testNewWorkerValidateConfig(c, "validating config: nil NewUpdater not valid")
    54  }
    55  
    56  func (s *WorkerSuite) TestNewWorkerValidateLocalClock(c *gc.C) {
    57  	s.config.LocalClock = nil
    58  	s.testNewWorkerValidateConfig(c, "validating config: nil LocalClock not valid")
    59  }
    60  
    61  func (s *WorkerSuite) TestNewWorkerValidateUpdateInterval(c *gc.C) {
    62  	s.config.UpdateInterval = 0
    63  	s.testNewWorkerValidateConfig(c, "validating config: non-positive UpdateInterval not valid")
    64  }
    65  
    66  func (s *WorkerSuite) TestNewWorkerValidateBackoffDelay(c *gc.C) {
    67  	s.config.BackoffDelay = -1
    68  	s.testNewWorkerValidateConfig(c, "validating config: non-positive BackoffDelay not valid")
    69  }
    70  
    71  func (s *WorkerSuite) testNewWorkerValidateConfig(c *gc.C, expect string) {
    72  	worker, err := globalclockupdater.NewWorker(s.config)
    73  	c.Check(err, gc.ErrorMatches, expect)
    74  	c.Check(worker, gc.IsNil)
    75  }
    76  
    77  func (s *WorkerSuite) TestNewWorkerNewUpdaterError(c *gc.C) {
    78  	s.stub.SetErrors(errors.New("nup"))
    79  	worker, err := globalclockupdater.NewWorker(s.config)
    80  	c.Check(err, gc.ErrorMatches, "getting new updater: nup")
    81  	c.Check(worker, gc.IsNil)
    82  }
    83  
    84  func (s *WorkerSuite) TestNewWorkerSuccess(c *gc.C) {
    85  	worker, err := globalclockupdater.NewWorker(s.config)
    86  	c.Check(err, jc.ErrorIsNil)
    87  	c.Check(worker, gc.NotNil)
    88  	defer workertest.CleanKill(c, worker)
    89  	s.stub.CheckCallNames(c, "NewUpdater")
    90  }
    91  
    92  func (s *WorkerSuite) TestWorkerUpdatesOnInterval(c *gc.C) {
    93  	worker, err := globalclockupdater.NewWorker(s.config)
    94  	c.Check(err, jc.ErrorIsNil)
    95  	c.Check(worker, gc.NotNil)
    96  	defer workertest.CleanKill(c, worker)
    97  
    98  	for i := 0; i < 2; i++ {
    99  		waitAdvance(c, s.localClock, 500*time.Millisecond)
   100  		select {
   101  		case <-s.updater.added:
   102  			c.Fatal("unexpected update")
   103  		case <-time.After(coretesting.ShortWait):
   104  		}
   105  
   106  		waitAdvance(c, s.localClock, 501*time.Millisecond)
   107  		select {
   108  		case d := <-s.updater.added:
   109  			c.Assert(d, gc.Equals, time.Second+time.Millisecond)
   110  		case <-time.After(coretesting.LongWait):
   111  			c.Fatal("timed out waiting for update")
   112  		}
   113  	}
   114  }
   115  
   116  func (s *WorkerSuite) TestWorkerBackoffOnConcurrentUpdate(c *gc.C) {
   117  	worker, err := globalclockupdater.NewWorker(s.config)
   118  	c.Check(err, jc.ErrorIsNil)
   119  	c.Check(worker, gc.NotNil)
   120  	defer workertest.CleanKill(c, worker)
   121  
   122  	s.updater.SetErrors(errors.Annotate(globalclock.ErrConcurrentUpdate, "context info"))
   123  
   124  	waitAdvance(c, s.localClock, time.Second)
   125  	select {
   126  	case d := <-s.updater.added:
   127  		c.Assert(d, gc.Equals, time.Second)
   128  	case <-time.After(coretesting.LongWait):
   129  		c.Fatal("timed out waiting for update")
   130  	}
   131  
   132  	// The worker should be waiting for the backoff delay
   133  	// before attempting another update.
   134  	waitAdvance(c, s.localClock, time.Second)
   135  	select {
   136  	case <-s.updater.added:
   137  		c.Fatal("unexpected update")
   138  	case <-time.After(coretesting.ShortWait):
   139  	}
   140  
   141  	waitAdvance(c, s.localClock, 59*time.Second)
   142  	select {
   143  	case d := <-s.updater.added:
   144  		c.Assert(d, gc.Equals, time.Minute)
   145  	case <-time.After(coretesting.LongWait):
   146  		c.Fatal("timed out waiting for update")
   147  	}
   148  }
   149  
   150  func (s *WorkerSuite) TestWorkerHandlesTimeout(c *gc.C) {
   151  	// At startup there's a time where the updater might return
   152  	// timeouts - we should handle this cleanly.
   153  	worker, err := globalclockupdater.NewWorker(s.config)
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	defer workertest.CleanKill(c, worker)
   156  
   157  	s.updater.SetErrors(errors.Annotate(globalclock.ErrTimeout, "some context"))
   158  
   159  	waitAdvance(c, s.localClock, time.Second)
   160  	select {
   161  	case d := <-s.updater.added:
   162  		c.Assert(d, gc.Equals, time.Second)
   163  	case <-time.After(coretesting.LongWait):
   164  		c.Fatal("timed out waiting for update")
   165  	}
   166  
   167  	// The worker should try again next time, adding the total missed
   168  	// time.
   169  	waitAdvance(c, s.localClock, time.Second)
   170  	select {
   171  	case d := <-s.updater.added:
   172  		c.Assert(d, gc.Equals, 2*time.Second)
   173  	case <-time.After(coretesting.LongWait):
   174  		c.Fatal("timed out waiting for update")
   175  	}
   176  }
   177  
   178  func (s *WorkerSuite) TestWorkerUpdateErrorStopsWorker(c *gc.C) {
   179  	worker, err := globalclockupdater.NewWorker(s.config)
   180  	c.Check(err, jc.ErrorIsNil)
   181  	c.Check(worker, gc.NotNil)
   182  	defer workertest.DirtyKill(c, worker)
   183  
   184  	s.updater.SetErrors(errors.New("burp"))
   185  	waitAdvance(c, s.localClock, time.Second)
   186  	err = workertest.CheckKilled(c, worker)
   187  	c.Assert(err, gc.ErrorMatches, "updating global clock: burp")
   188  }
   189  
   190  type stubUpdater struct {
   191  	testing.Stub
   192  	added chan time.Duration
   193  }
   194  
   195  func (s *stubUpdater) Advance(d time.Duration) error {
   196  	s.MethodCall(s, "Advance", d)
   197  	s.added <- d
   198  	return s.NextErr()
   199  }
   200  
   201  func waitAdvance(c *gc.C, clock *testclock.Clock, d time.Duration) {
   202  	err := clock.WaitAdvance(d, time.Second, 1)
   203  	c.Assert(err, jc.ErrorIsNil)
   204  }