github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/presence/presence_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package presence_test 5 6 import ( 7 "strconv" 8 stdtesting "testing" 9 "time" 10 11 "labix.org/v2/mgo" 12 gc "launchpad.net/gocheck" 13 "launchpad.net/tomb" 14 15 "github.com/juju/juju/state/presence" 16 "github.com/juju/juju/testing" 17 ) 18 19 func TestPackage(t *stdtesting.T) { 20 testing.MgoTestPackage(t) 21 } 22 23 type PresenceSuite struct { 24 testing.MgoSuite 25 testing.BaseSuite 26 presence *mgo.Collection 27 pings *mgo.Collection 28 } 29 30 var _ = gc.Suite(&PresenceSuite{}) 31 32 func (s *PresenceSuite) SetUpSuite(c *gc.C) { 33 s.BaseSuite.SetUpSuite(c) 34 s.MgoSuite.SetUpSuite(c) 35 } 36 37 func (s *PresenceSuite) TearDownSuite(c *gc.C) { 38 s.MgoSuite.TearDownSuite(c) 39 s.BaseSuite.TearDownSuite(c) 40 } 41 42 func (s *PresenceSuite) SetUpTest(c *gc.C) { 43 s.BaseSuite.SetUpTest(c) 44 s.MgoSuite.SetUpTest(c) 45 46 db := s.MgoSuite.Session.DB("presence") 47 s.presence = db.C("presence") 48 s.pings = db.C("presence.pings") 49 50 presence.FakeTimeSlot(0) 51 } 52 53 func (s *PresenceSuite) TearDownTest(c *gc.C) { 54 s.MgoSuite.TearDownTest(c) 55 s.BaseSuite.TearDownTest(c) 56 57 presence.RealTimeSlot() 58 presence.RealPeriod() 59 } 60 61 func assertChange(c *gc.C, watch <-chan presence.Change, want presence.Change) { 62 select { 63 case got := <-watch: 64 if got != want { 65 c.Fatalf("watch reported %v, want %v", got, want) 66 } 67 case <-time.After(testing.LongWait): 68 c.Fatalf("watch reported nothing, want %v", want) 69 } 70 } 71 72 func assertNoChange(c *gc.C, watch <-chan presence.Change) { 73 select { 74 case got := <-watch: 75 c.Fatalf("watch reported %v, want nothing", got) 76 case <-time.After(testing.ShortWait): 77 } 78 } 79 80 func assertAlive(c *gc.C, w *presence.Watcher, key string, alive bool) { 81 alive, err := w.Alive("a") 82 c.Assert(err, gc.IsNil) 83 c.Assert(alive, gc.Equals, alive) 84 } 85 86 func (s *PresenceSuite) TestErrAndDead(c *gc.C) { 87 w := presence.NewWatcher(s.presence) 88 defer w.Stop() 89 90 c.Assert(w.Err(), gc.Equals, tomb.ErrStillAlive) 91 select { 92 case <-w.Dead(): 93 c.Fatalf("Dead channel fired unexpectedly") 94 default: 95 } 96 c.Assert(w.Stop(), gc.IsNil) 97 c.Assert(w.Err(), gc.IsNil) 98 select { 99 case <-w.Dead(): 100 default: 101 c.Fatalf("Dead channel should have fired") 102 } 103 } 104 105 func (s *PresenceSuite) TestAliveError(c *gc.C) { 106 w := presence.NewWatcher(s.presence) 107 c.Assert(w.Stop(), gc.IsNil) 108 109 alive, err := w.Alive("a") 110 c.Assert(err, gc.ErrorMatches, ".*: watcher is dying") 111 c.Assert(alive, gc.Equals, false) 112 } 113 114 func (s *PresenceSuite) TestWorkflow(c *gc.C) { 115 w := presence.NewWatcher(s.presence) 116 pa := presence.NewPinger(s.presence, "a") 117 pb := presence.NewPinger(s.presence, "b") 118 defer w.Stop() 119 defer pa.Stop() 120 defer pb.Stop() 121 122 assertAlive(c, w, "a", false) 123 assertAlive(c, w, "b", false) 124 125 // Buffer one entry to avoid blocking the watcher here. 126 cha := make(chan presence.Change, 1) 127 chb := make(chan presence.Change, 1) 128 w.Watch("a", cha) 129 w.Watch("b", chb) 130 131 // Initial events with current status. 132 assertChange(c, cha, presence.Change{"a", false}) 133 assertChange(c, chb, presence.Change{"b", false}) 134 135 w.StartSync() 136 assertNoChange(c, cha) 137 assertNoChange(c, chb) 138 139 c.Assert(pa.Start(), gc.IsNil) 140 141 w.StartSync() 142 assertChange(c, cha, presence.Change{"a", true}) 143 assertNoChange(c, cha) 144 assertNoChange(c, chb) 145 146 assertAlive(c, w, "a", true) 147 assertAlive(c, w, "b", false) 148 149 // Changes while the channel is out are not observed. 150 w.Unwatch("a", cha) 151 assertNoChange(c, cha) 152 pa.Kill() 153 w.Sync() 154 pa = presence.NewPinger(s.presence, "a") 155 pa.Start() 156 w.StartSync() 157 assertNoChange(c, cha) 158 159 // We can still query it manually, though. 160 assertAlive(c, w, "a", true) 161 assertAlive(c, w, "b", false) 162 163 // Initial positive event. No refresh needed. 164 w.Watch("a", cha) 165 assertChange(c, cha, presence.Change{"a", true}) 166 167 c.Assert(pb.Start(), gc.IsNil) 168 169 w.StartSync() 170 assertChange(c, chb, presence.Change{"b", true}) 171 assertNoChange(c, cha) 172 assertNoChange(c, chb) 173 174 c.Assert(pa.Stop(), gc.IsNil) 175 176 w.StartSync() 177 assertNoChange(c, cha) 178 assertNoChange(c, chb) 179 180 // pb is running, pa isn't. 181 c.Assert(pa.Kill(), gc.IsNil) 182 c.Assert(pb.Kill(), gc.IsNil) 183 184 w.StartSync() 185 assertChange(c, cha, presence.Change{"a", false}) 186 assertChange(c, chb, presence.Change{"b", false}) 187 188 c.Assert(w.Stop(), gc.IsNil) 189 } 190 191 func (s *PresenceSuite) TestScale(c *gc.C) { 192 const N = 1000 193 var ps []*presence.Pinger 194 defer func() { 195 for _, p := range ps { 196 p.Stop() 197 } 198 }() 199 200 c.Logf("Starting %d pingers...", N) 201 for i := 0; i < N; i++ { 202 p := presence.NewPinger(s.presence, strconv.Itoa(i)) 203 c.Assert(p.Start(), gc.IsNil) 204 ps = append(ps, p) 205 } 206 207 c.Logf("Killing odd ones...") 208 for i := 1; i < N; i += 2 { 209 c.Assert(ps[i].Kill(), gc.IsNil) 210 } 211 212 c.Logf("Checking who's still alive...") 213 w := presence.NewWatcher(s.presence) 214 defer w.Stop() 215 w.Sync() 216 ch := make(chan presence.Change) 217 for i := 0; i < N; i++ { 218 k := strconv.Itoa(i) 219 w.Watch(k, ch) 220 if i%2 == 0 { 221 assertChange(c, ch, presence.Change{k, true}) 222 } else { 223 assertChange(c, ch, presence.Change{k, false}) 224 } 225 } 226 } 227 228 func (s *PresenceSuite) TestExpiry(c *gc.C) { 229 w := presence.NewWatcher(s.presence) 230 p := presence.NewPinger(s.presence, "a") 231 defer w.Stop() 232 defer p.Stop() 233 234 ch := make(chan presence.Change) 235 w.Watch("a", ch) 236 assertChange(c, ch, presence.Change{"a", false}) 237 238 c.Assert(p.Start(), gc.IsNil) 239 w.StartSync() 240 assertChange(c, ch, presence.Change{"a", true}) 241 242 // Still alive in previous slot. 243 presence.FakeTimeSlot(1) 244 w.StartSync() 245 assertNoChange(c, ch) 246 247 // Two last slots are empty. 248 presence.FakeTimeSlot(2) 249 w.StartSync() 250 assertChange(c, ch, presence.Change{"a", false}) 251 252 // Already dead so killing isn't noticed. 253 p.Kill() 254 w.StartSync() 255 assertNoChange(c, ch) 256 } 257 258 func (s *PresenceSuite) TestWatchPeriod(c *gc.C) { 259 presence.FakePeriod(1) 260 presence.RealTimeSlot() 261 262 w := presence.NewWatcher(s.presence) 263 p := presence.NewPinger(s.presence, "a") 264 defer w.Stop() 265 defer p.Stop() 266 267 ch := make(chan presence.Change) 268 w.Watch("a", ch) 269 assertChange(c, ch, presence.Change{"a", false}) 270 271 // A single ping. 272 c.Assert(p.Start(), gc.IsNil) 273 c.Assert(p.Stop(), gc.IsNil) 274 275 // Wait for next periodic refresh. 276 time.Sleep(1 * time.Second) 277 assertChange(c, ch, presence.Change{"a", true}) 278 } 279 280 func (s *PresenceSuite) TestWatchUnwatchOnQueue(c *gc.C) { 281 w := presence.NewWatcher(s.presence) 282 ch := make(chan presence.Change) 283 for i := 0; i < 100; i++ { 284 key := strconv.Itoa(i) 285 c.Logf("Adding %q", key) 286 w.Watch(key, ch) 287 } 288 for i := 1; i < 100; i += 2 { 289 key := strconv.Itoa(i) 290 c.Logf("Removing %q", key) 291 w.Unwatch(key, ch) 292 } 293 alive := make(map[string]bool) 294 for i := 0; i < 50; i++ { 295 change := <-ch 296 c.Logf("Got change for %q: %v", change.Key, change.Alive) 297 alive[change.Key] = change.Alive 298 } 299 for i := 0; i < 100; i += 2 { 300 key := strconv.Itoa(i) 301 c.Logf("Checking %q...", key) 302 c.Assert(alive[key], gc.Equals, false) 303 } 304 } 305 306 func (s *PresenceSuite) TestRestartWithoutGaps(c *gc.C) { 307 p := presence.NewPinger(s.presence, "a") 308 c.Assert(p.Start(), gc.IsNil) 309 defer p.Stop() 310 311 done := make(chan bool) 312 go func() { 313 stop := false 314 for !stop { 315 if !c.Check(p.Stop(), gc.IsNil) { 316 break 317 } 318 if !c.Check(p.Start(), gc.IsNil) { 319 break 320 } 321 select { 322 case stop = <-done: 323 default: 324 } 325 } 326 }() 327 go func() { 328 stop := false 329 for !stop { 330 w := presence.NewWatcher(s.presence) 331 w.Sync() 332 alive, err := w.Alive("a") 333 c.Check(w.Stop(), gc.IsNil) 334 if !c.Check(err, gc.IsNil) || !c.Check(alive, gc.Equals, true) { 335 break 336 } 337 select { 338 case stop = <-done: 339 default: 340 } 341 } 342 }() 343 // TODO(jam): This forceful delay of 500ms sounds like a bad test, 344 // since we always sleep for the full timeout 345 time.Sleep(500 * time.Millisecond) 346 done <- true 347 done <- true 348 } 349 350 func (s *PresenceSuite) TestPingerPeriodAndResilience(c *gc.C) { 351 // This test verifies both the periodic pinging, 352 // and also a great property of the design: deaths 353 // also expire, which means erroneous scenarios are 354 // automatically recovered from. 355 356 const period = 1 357 presence.FakePeriod(period) 358 presence.RealTimeSlot() 359 360 w := presence.NewWatcher(s.presence) 361 p1 := presence.NewPinger(s.presence, "a") 362 p2 := presence.NewPinger(s.presence, "a") 363 defer w.Stop() 364 defer p1.Stop() 365 defer p2.Stop() 366 367 // Start p1 and let it go on. 368 c.Assert(p1.Start(), gc.IsNil) 369 370 w.Sync() 371 assertAlive(c, w, "a", true) 372 373 // Start and kill p2, which will temporarily 374 // invalidate p1 and set the key as dead. 375 c.Assert(p2.Start(), gc.IsNil) 376 c.Assert(p2.Kill(), gc.IsNil) 377 378 w.Sync() 379 assertAlive(c, w, "a", false) 380 381 // Wait for two periods, and check again. Since 382 // p1 is still alive, p2's death will expire and 383 // the key will come back. 384 time.Sleep(period * 2 * time.Second) 385 386 w.Sync() 387 assertAlive(c, w, "a", true) 388 } 389 390 func (s *PresenceSuite) TestStartSync(c *gc.C) { 391 w := presence.NewWatcher(s.presence) 392 p := presence.NewPinger(s.presence, "a") 393 defer w.Stop() 394 defer p.Stop() 395 396 ch := make(chan presence.Change) 397 w.Watch("a", ch) 398 assertChange(c, ch, presence.Change{"a", false}) 399 400 c.Assert(p.Start(), gc.IsNil) 401 402 done := make(chan bool) 403 go func() { 404 w.StartSync() 405 w.StartSync() 406 w.StartSync() 407 done <- true 408 }() 409 410 select { 411 case <-done: 412 case <-time.After(testing.LongWait): 413 c.Fatalf("StartSync failed to return") 414 } 415 416 assertChange(c, ch, presence.Change{"a", true}) 417 } 418 419 func (s *PresenceSuite) TestSync(c *gc.C) { 420 w := presence.NewWatcher(s.presence) 421 p := presence.NewPinger(s.presence, "a") 422 defer w.Stop() 423 defer p.Stop() 424 425 ch := make(chan presence.Change) 426 w.Watch("a", ch) 427 assertChange(c, ch, presence.Change{"a", false}) 428 429 // Nothing to do here. 430 w.Sync() 431 432 c.Assert(p.Start(), gc.IsNil) 433 434 done := make(chan bool) 435 go func() { 436 w.Sync() 437 done <- true 438 }() 439 440 select { 441 case <-done: 442 c.Fatalf("Sync returned too early") 443 // Note(jam): This used to wait 200ms to ensure that 444 // Sync was actually blocked waiting for a presence 445 // change. Is ShortWait long enough for this assurance? 446 case <-time.After(testing.ShortWait): 447 } 448 449 assertChange(c, ch, presence.Change{"a", true}) 450 451 select { 452 case <-done: 453 case <-time.After(testing.LongWait): 454 c.Fatalf("Sync failed to returned") 455 } 456 } 457 458 func (s *PresenceSuite) TestFindAllBeings(c *gc.C) { 459 w := presence.NewWatcher(s.presence) 460 p := presence.NewPinger(s.presence, "a") 461 defer w.Stop() 462 defer p.Stop() 463 464 ch := make(chan presence.Change) 465 w.Watch("a", ch) 466 assertChange(c, ch, presence.Change{"a", false}) 467 c.Assert(p.Start(), gc.IsNil) 468 done := make(chan bool) 469 go func() { 470 w.Sync() 471 done <- true 472 }() 473 assertChange(c, ch, presence.Change{"a", true}) 474 results, err := presence.FindAllBeings(w) 475 c.Assert(err, gc.IsNil) 476 c.Assert(results, gc.HasLen, 1) 477 select { 478 case <-done: 479 case <-time.After(testing.LongWait): 480 c.Fatalf("Sync failed to returned") 481 } 482 }