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 }