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 }