github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/state/multiwatcher/multiwatcher_internal_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package multiwatcher 5 6 import ( 7 "container/list" 8 "errors" 9 "fmt" 10 "sync" 11 stdtesting "testing" 12 "time" 13 14 "labix.org/v2/mgo" 15 gc "launchpad.net/gocheck" 16 17 "launchpad.net/juju-core/state/api/params" 18 "launchpad.net/juju-core/state/watcher" 19 "launchpad.net/juju-core/testing/testbase" 20 ) 21 22 func Test(t *stdtesting.T) { 23 gc.TestingT(t) 24 } 25 26 type storeSuite struct { 27 testbase.LoggingSuite 28 } 29 30 var _ = gc.Suite(&storeSuite{}) 31 32 var StoreChangeMethodTests = []struct { 33 about string 34 change func(all *Store) 35 expectRevno int64 36 expectContents []entityEntry 37 }{{ 38 about: "empty at first", 39 change: func(*Store) {}, 40 }, { 41 about: "add single entry", 42 change: func(all *Store) { 43 all.Update(&MachineInfo{ 44 Id: "0", 45 InstanceId: "i-0", 46 }) 47 }, 48 expectRevno: 1, 49 expectContents: []entityEntry{{ 50 creationRevno: 1, 51 revno: 1, 52 info: &MachineInfo{ 53 Id: "0", 54 InstanceId: "i-0", 55 }, 56 }}, 57 }, { 58 about: "add two entries", 59 change: func(all *Store) { 60 all.Update(&MachineInfo{ 61 Id: "0", 62 InstanceId: "i-0", 63 }) 64 all.Update(&ServiceInfo{ 65 Name: "wordpress", 66 Exposed: true, 67 }) 68 }, 69 expectRevno: 2, 70 expectContents: []entityEntry{{ 71 creationRevno: 1, 72 revno: 1, 73 info: &MachineInfo{ 74 Id: "0", 75 InstanceId: "i-0", 76 }, 77 }, { 78 creationRevno: 2, 79 revno: 2, 80 info: &ServiceInfo{ 81 Name: "wordpress", 82 Exposed: true, 83 }, 84 }}, 85 }, { 86 about: "update an entity that's not currently there", 87 change: func(all *Store) { 88 m := &MachineInfo{Id: "1"} 89 all.Update(m) 90 }, 91 expectRevno: 1, 92 expectContents: []entityEntry{{ 93 creationRevno: 1, 94 revno: 1, 95 info: &MachineInfo{Id: "1"}, 96 }}, 97 }, { 98 about: "mark removed on existing entry", 99 change: func(all *Store) { 100 all.Update(&MachineInfo{Id: "0"}) 101 all.Update(&MachineInfo{Id: "1"}) 102 StoreIncRef(all, params.EntityId{"machine", "0"}) 103 all.Remove(params.EntityId{"machine", "0"}) 104 }, 105 expectRevno: 3, 106 expectContents: []entityEntry{{ 107 creationRevno: 2, 108 revno: 2, 109 info: &MachineInfo{Id: "1"}, 110 }, { 111 creationRevno: 1, 112 revno: 3, 113 refCount: 1, 114 removed: true, 115 info: &MachineInfo{Id: "0"}, 116 }}, 117 }, { 118 about: "mark removed on nonexistent entry", 119 change: func(all *Store) { 120 all.Remove(params.EntityId{"machine", "0"}) 121 }, 122 }, { 123 about: "mark removed on already marked entry", 124 change: func(all *Store) { 125 all.Update(&MachineInfo{Id: "0"}) 126 all.Update(&MachineInfo{Id: "1"}) 127 StoreIncRef(all, params.EntityId{"machine", "0"}) 128 all.Remove(params.EntityId{"machine", "0"}) 129 all.Update(&MachineInfo{ 130 Id: "1", 131 InstanceId: "i-1", 132 }) 133 all.Remove(params.EntityId{"machine", "0"}) 134 }, 135 expectRevno: 4, 136 expectContents: []entityEntry{{ 137 creationRevno: 1, 138 revno: 3, 139 refCount: 1, 140 removed: true, 141 info: &MachineInfo{Id: "0"}, 142 }, { 143 creationRevno: 2, 144 revno: 4, 145 info: &MachineInfo{ 146 Id: "1", 147 InstanceId: "i-1", 148 }, 149 }}, 150 }, { 151 about: "mark removed on entry with zero ref count", 152 change: func(all *Store) { 153 all.Update(&MachineInfo{Id: "0"}) 154 all.Remove(params.EntityId{"machine", "0"}) 155 }, 156 expectRevno: 2, 157 }, { 158 about: "delete entry", 159 change: func(all *Store) { 160 all.Update(&MachineInfo{Id: "0"}) 161 all.delete(params.EntityId{"machine", "0"}) 162 }, 163 expectRevno: 1, 164 }, { 165 about: "decref of non-removed entity", 166 change: func(all *Store) { 167 m := &MachineInfo{Id: "0"} 168 all.Update(m) 169 id := m.EntityId() 170 StoreIncRef(all, id) 171 entry := all.entities[id].Value.(*entityEntry) 172 all.decRef(entry) 173 }, 174 expectRevno: 1, 175 expectContents: []entityEntry{{ 176 creationRevno: 1, 177 revno: 1, 178 refCount: 0, 179 info: &MachineInfo{Id: "0"}, 180 }}, 181 }, { 182 about: "decref of removed entity", 183 change: func(all *Store) { 184 m := &MachineInfo{Id: "0"} 185 all.Update(m) 186 id := m.EntityId() 187 entry := all.entities[id].Value.(*entityEntry) 188 entry.refCount++ 189 all.Remove(id) 190 all.decRef(entry) 191 }, 192 expectRevno: 2, 193 }, 194 } 195 196 func (s *storeSuite) TestStoreChangeMethods(c *gc.C) { 197 for i, test := range StoreChangeMethodTests { 198 all := NewStore() 199 c.Logf("test %d. %s", i, test.about) 200 test.change(all) 201 assertStoreContents(c, all, test.expectRevno, test.expectContents) 202 } 203 } 204 205 func (s *storeSuite) TestChangesSince(c *gc.C) { 206 a := NewStore() 207 // Add three entries. 208 var deltas []params.Delta 209 for i := 0; i < 3; i++ { 210 m := &MachineInfo{Id: fmt.Sprint(i)} 211 a.Update(m) 212 deltas = append(deltas, params.Delta{Entity: m}) 213 } 214 // Check that the deltas from each revno are as expected. 215 for i := 0; i < 3; i++ { 216 c.Logf("test %d", i) 217 c.Assert(a.ChangesSince(int64(i)), gc.DeepEquals, deltas[i:]) 218 } 219 220 // Check boundary cases. 221 c.Assert(a.ChangesSince(-1), gc.DeepEquals, deltas) 222 c.Assert(a.ChangesSince(99), gc.HasLen, 0) 223 224 // Update one machine and check we see the changes. 225 rev := a.latestRevno 226 m1 := &MachineInfo{ 227 Id: "1", 228 InstanceId: "foo", 229 } 230 a.Update(m1) 231 c.Assert(a.ChangesSince(rev), gc.DeepEquals, []params.Delta{{Entity: m1}}) 232 233 // Make sure the machine isn't simply removed from 234 // the list when it's marked as removed. 235 StoreIncRef(a, params.EntityId{"machine", "0"}) 236 237 // Remove another machine and check we see it's removed. 238 m0 := &MachineInfo{Id: "0"} 239 a.Remove(m0.EntityId()) 240 241 // Check that something that never saw m0 does not get 242 // informed of its removal (even those the removed entity 243 // is still in the list. 244 c.Assert(a.ChangesSince(0), gc.DeepEquals, []params.Delta{{ 245 Entity: &MachineInfo{Id: "2"}, 246 }, { 247 Entity: m1, 248 }}) 249 250 c.Assert(a.ChangesSince(rev), gc.DeepEquals, []params.Delta{{ 251 Entity: m1, 252 }, { 253 Removed: true, 254 Entity: m0, 255 }}) 256 257 c.Assert(a.ChangesSince(rev+1), gc.DeepEquals, []params.Delta{{ 258 Removed: true, 259 Entity: m0, 260 }}) 261 } 262 263 func (s *storeSuite) TestGet(c *gc.C) { 264 a := NewStore() 265 m := &MachineInfo{Id: "0"} 266 a.Update(m) 267 268 c.Assert(a.Get(m.EntityId()), gc.Equals, m) 269 c.Assert(a.Get(params.EntityId{"machine", "1"}), gc.IsNil) 270 } 271 272 type storeManagerSuite struct { 273 testbase.LoggingSuite 274 } 275 276 var _ = gc.Suite(&storeManagerSuite{}) 277 278 func (*storeManagerSuite) TestHandle(c *gc.C) { 279 sm := newStoreManagerNoRun(newTestBacking(nil)) 280 281 // Add request from first watcher. 282 w0 := &Watcher{all: sm} 283 req0 := &request{ 284 w: w0, 285 reply: make(chan bool, 1), 286 } 287 sm.handle(req0) 288 assertWaitingRequests(c, sm, map[*Watcher][]*request{ 289 w0: {req0}, 290 }) 291 292 // Add second request from first watcher. 293 req1 := &request{ 294 w: w0, 295 reply: make(chan bool, 1), 296 } 297 sm.handle(req1) 298 assertWaitingRequests(c, sm, map[*Watcher][]*request{ 299 w0: {req1, req0}, 300 }) 301 302 // Add request from second watcher. 303 w1 := &Watcher{all: sm} 304 req2 := &request{ 305 w: w1, 306 reply: make(chan bool, 1), 307 } 308 sm.handle(req2) 309 assertWaitingRequests(c, sm, map[*Watcher][]*request{ 310 w0: {req1, req0}, 311 w1: {req2}, 312 }) 313 314 // Stop first watcher. 315 sm.handle(&request{ 316 w: w0, 317 }) 318 assertWaitingRequests(c, sm, map[*Watcher][]*request{ 319 w1: {req2}, 320 }) 321 assertReplied(c, false, req0) 322 assertReplied(c, false, req1) 323 324 // Stop second watcher. 325 sm.handle(&request{ 326 w: w1, 327 }) 328 assertWaitingRequests(c, sm, nil) 329 assertReplied(c, false, req2) 330 } 331 332 func (s *storeManagerSuite) TestHandleStopNoDecRefIfMoreRecentlyCreated(c *gc.C) { 333 // If the Watcher hasn't seen the item, then we shouldn't 334 // decrement its ref count when it is stopped. 335 sm := NewStoreManager(newTestBacking(nil)) 336 sm.all.Update(&MachineInfo{Id: "0"}) 337 StoreIncRef(sm.all, params.EntityId{"machine", "0"}) 338 w := &Watcher{all: sm} 339 340 // Stop the watcher. 341 sm.handle(&request{w: w}) 342 assertStoreContents(c, sm.all, 1, []entityEntry{{ 343 creationRevno: 1, 344 revno: 1, 345 refCount: 1, 346 info: &MachineInfo{ 347 Id: "0", 348 }, 349 }}) 350 } 351 352 func (s *storeManagerSuite) TestHandleStopNoDecRefIfAlreadySeenRemoved(c *gc.C) { 353 // If the Watcher has already seen the item removed, then 354 // we shouldn't decrement its ref count when it is stopped. 355 sm := NewStoreManager(newTestBacking(nil)) 356 sm.all.Update(&MachineInfo{Id: "0"}) 357 StoreIncRef(sm.all, params.EntityId{"machine", "0"}) 358 sm.all.Remove(params.EntityId{"machine", "0"}) 359 w := &Watcher{all: sm} 360 // Stop the watcher. 361 sm.handle(&request{w: w}) 362 assertStoreContents(c, sm.all, 2, []entityEntry{{ 363 creationRevno: 1, 364 revno: 2, 365 refCount: 1, 366 removed: true, 367 info: &MachineInfo{ 368 Id: "0", 369 }, 370 }}) 371 } 372 373 func (s *storeManagerSuite) TestHandleStopDecRefIfAlreadySeenAndNotRemoved(c *gc.C) { 374 // If the Watcher has already seen the item removed, then 375 // we should decrement its ref count when it is stopped. 376 sm := NewStoreManager(newTestBacking(nil)) 377 sm.all.Update(&MachineInfo{Id: "0"}) 378 StoreIncRef(sm.all, params.EntityId{"machine", "0"}) 379 w := &Watcher{all: sm} 380 w.revno = sm.all.latestRevno 381 // Stop the watcher. 382 sm.handle(&request{w: w}) 383 assertStoreContents(c, sm.all, 1, []entityEntry{{ 384 creationRevno: 1, 385 revno: 1, 386 info: &MachineInfo{ 387 Id: "0", 388 }, 389 }}) 390 } 391 392 func (s *storeManagerSuite) TestHandleStopNoDecRefIfNotSeen(c *gc.C) { 393 // If the Watcher hasn't seen the item at all, it should 394 // leave the ref count untouched. 395 sm := NewStoreManager(newTestBacking(nil)) 396 sm.all.Update(&MachineInfo{Id: "0"}) 397 StoreIncRef(sm.all, params.EntityId{"machine", "0"}) 398 w := &Watcher{all: sm} 399 // Stop the watcher. 400 sm.handle(&request{w: w}) 401 assertStoreContents(c, sm.all, 1, []entityEntry{{ 402 creationRevno: 1, 403 revno: 1, 404 refCount: 1, 405 info: &MachineInfo{ 406 Id: "0", 407 }, 408 }}) 409 } 410 411 var respondTestChanges = [...]func(all *Store){ 412 func(all *Store) { 413 all.Update(&MachineInfo{Id: "0"}) 414 }, 415 func(all *Store) { 416 all.Update(&MachineInfo{Id: "1"}) 417 }, 418 func(all *Store) { 419 all.Update(&MachineInfo{Id: "2"}) 420 }, 421 func(all *Store) { 422 all.Remove(params.EntityId{"machine", "0"}) 423 }, 424 func(all *Store) { 425 all.Update(&MachineInfo{ 426 Id: "1", 427 InstanceId: "i-1", 428 }) 429 }, 430 func(all *Store) { 431 all.Remove(params.EntityId{"machine", "1"}) 432 }, 433 } 434 435 var ( 436 respondTestFinalState = []entityEntry{{ 437 creationRevno: 3, 438 revno: 3, 439 info: &MachineInfo{ 440 Id: "2", 441 }, 442 }} 443 respondTestFinalRevno = int64(len(respondTestChanges)) 444 ) 445 446 func (s *storeManagerSuite) TestRespondResults(c *gc.C) { 447 // We test the response results for a pair of watchers by 448 // interleaving notional Next requests in all possible 449 // combinations after each change in respondTestChanges and 450 // checking that the view of the world as seen by the watchers 451 // matches the actual current state. 452 453 // We decide whether if we make a request for a given 454 // watcher by inspecting a number n - bit i of n determines whether 455 // a request will be responded to after running respondTestChanges[i]. 456 457 numCombinations := 1 << uint(len(respondTestChanges)) 458 const wcount = 2 459 ns := make([]int, wcount) 460 for ns[0] = 0; ns[0] < numCombinations; ns[0]++ { 461 for ns[1] = 0; ns[1] < numCombinations; ns[1]++ { 462 sm := newStoreManagerNoRun(&storeManagerTestBacking{}) 463 c.Logf("test %0*b", len(respondTestChanges), ns) 464 var ( 465 ws []*Watcher 466 wstates []watcherState 467 reqs []*request 468 ) 469 for i := 0; i < wcount; i++ { 470 ws = append(ws, &Watcher{}) 471 wstates = append(wstates, make(watcherState)) 472 reqs = append(reqs, nil) 473 } 474 // Make each change in turn, and make a request for each 475 // watcher if n and respond 476 for i, change := range respondTestChanges { 477 c.Logf("change %d", i) 478 change(sm.all) 479 needRespond := false 480 for wi, n := range ns { 481 if n&(1<<uint(i)) != 0 { 482 needRespond = true 483 if reqs[wi] == nil { 484 reqs[wi] = &request{ 485 w: ws[wi], 486 reply: make(chan bool, 1), 487 } 488 sm.handle(reqs[wi]) 489 } 490 } 491 } 492 if !needRespond { 493 continue 494 } 495 // Check that the expected requests are pending. 496 expectWaiting := make(map[*Watcher][]*request) 497 for wi, w := range ws { 498 if reqs[wi] != nil { 499 expectWaiting[w] = []*request{reqs[wi]} 500 } 501 } 502 assertWaitingRequests(c, sm, expectWaiting) 503 // Actually respond; then check that each watcher with 504 // an outstanding request now has an up to date view 505 // of the world. 506 sm.respond() 507 for wi, req := range reqs { 508 if req == nil { 509 continue 510 } 511 select { 512 case ok := <-req.reply: 513 c.Assert(ok, gc.Equals, true) 514 c.Assert(len(req.changes) > 0, gc.Equals, true) 515 wstates[wi].update(req.changes) 516 reqs[wi] = nil 517 default: 518 } 519 c.Logf("check %d", wi) 520 wstates[wi].check(c, sm.all) 521 } 522 } 523 // Stop the watcher and check that all ref counts end up at zero 524 // and removed objects are deleted. 525 for wi, w := range ws { 526 sm.handle(&request{w: w}) 527 if reqs[wi] != nil { 528 assertReplied(c, false, reqs[wi]) 529 } 530 } 531 assertStoreContents(c, sm.all, respondTestFinalRevno, respondTestFinalState) 532 } 533 } 534 } 535 536 func (*storeManagerSuite) TestRespondMultiple(c *gc.C) { 537 sm := NewStoreManager(newTestBacking(nil)) 538 sm.all.Update(&MachineInfo{Id: "0"}) 539 540 // Add one request and respond. 541 // It should see the above change. 542 w0 := &Watcher{all: sm} 543 req0 := &request{ 544 w: w0, 545 reply: make(chan bool, 1), 546 } 547 sm.handle(req0) 548 sm.respond() 549 assertReplied(c, true, req0) 550 c.Assert(req0.changes, gc.DeepEquals, []params.Delta{{Entity: &MachineInfo{Id: "0"}}}) 551 assertWaitingRequests(c, sm, nil) 552 553 // Add another request from the same watcher and respond. 554 // It should have no reply because nothing has changed. 555 req0 = &request{ 556 w: w0, 557 reply: make(chan bool, 1), 558 } 559 sm.handle(req0) 560 sm.respond() 561 assertNotReplied(c, req0) 562 563 // Add two requests from another watcher and respond. 564 // The request from the first watcher should still not 565 // be replied to, but the later of the two requests from 566 // the second watcher should get a reply. 567 w1 := &Watcher{all: sm} 568 req1 := &request{ 569 w: w1, 570 reply: make(chan bool, 1), 571 } 572 sm.handle(req1) 573 req2 := &request{ 574 w: w1, 575 reply: make(chan bool, 1), 576 } 577 sm.handle(req2) 578 assertWaitingRequests(c, sm, map[*Watcher][]*request{ 579 w0: {req0}, 580 w1: {req2, req1}, 581 }) 582 sm.respond() 583 assertNotReplied(c, req0) 584 assertNotReplied(c, req1) 585 assertReplied(c, true, req2) 586 c.Assert(req2.changes, gc.DeepEquals, []params.Delta{{Entity: &MachineInfo{Id: "0"}}}) 587 assertWaitingRequests(c, sm, map[*Watcher][]*request{ 588 w0: {req0}, 589 w1: {req1}, 590 }) 591 592 // Check that nothing more gets responded to if we call respond again. 593 sm.respond() 594 assertNotReplied(c, req0) 595 assertNotReplied(c, req1) 596 597 // Now make a change and check that both waiting requests 598 // get serviced. 599 sm.all.Update(&MachineInfo{Id: "1"}) 600 sm.respond() 601 assertReplied(c, true, req0) 602 assertReplied(c, true, req1) 603 assertWaitingRequests(c, sm, nil) 604 605 deltas := []params.Delta{{Entity: &MachineInfo{Id: "1"}}} 606 c.Assert(req0.changes, gc.DeepEquals, deltas) 607 c.Assert(req1.changes, gc.DeepEquals, deltas) 608 } 609 610 func (*storeManagerSuite) TestRunStop(c *gc.C) { 611 sm := NewStoreManager(newTestBacking(nil)) 612 w := &Watcher{all: sm} 613 err := sm.Stop() 614 c.Assert(err, gc.IsNil) 615 d, err := w.Next() 616 c.Assert(err, gc.ErrorMatches, "shared state watcher was stopped") 617 c.Assert(d, gc.HasLen, 0) 618 } 619 620 func (*storeManagerSuite) TestRun(c *gc.C) { 621 b := newTestBacking([]params.EntityInfo{ 622 &MachineInfo{Id: "0"}, 623 &ServiceInfo{Name: "logging"}, 624 &ServiceInfo{Name: "wordpress"}, 625 }) 626 sm := NewStoreManager(b) 627 defer func() { 628 c.Check(sm.Stop(), gc.IsNil) 629 }() 630 w := &Watcher{all: sm} 631 checkNext(c, w, []params.Delta{ 632 {Entity: &MachineInfo{Id: "0"}}, 633 {Entity: &ServiceInfo{Name: "logging"}}, 634 {Entity: &ServiceInfo{Name: "wordpress"}}, 635 }, "") 636 b.updateEntity(&MachineInfo{Id: "0", InstanceId: "i-0"}) 637 checkNext(c, w, []params.Delta{ 638 {Entity: &MachineInfo{Id: "0", InstanceId: "i-0"}}, 639 }, "") 640 b.deleteEntity(params.EntityId{"machine", "0"}) 641 checkNext(c, w, []params.Delta{ 642 {Removed: true, Entity: &MachineInfo{Id: "0"}}, 643 }, "") 644 } 645 646 func (*storeManagerSuite) TestWatcherStop(c *gc.C) { 647 sm := NewStoreManager(newTestBacking(nil)) 648 defer func() { 649 c.Check(sm.Stop(), gc.IsNil) 650 }() 651 w := &Watcher{all: sm} 652 done := make(chan struct{}) 653 go func() { 654 checkNext(c, w, nil, ErrWatcherStopped.Error()) 655 done <- struct{}{} 656 }() 657 err := w.Stop() 658 c.Assert(err, gc.IsNil) 659 <-done 660 } 661 662 func (*storeManagerSuite) TestWatcherStopBecauseStoreManagerError(c *gc.C) { 663 b := newTestBacking([]params.EntityInfo{&MachineInfo{Id: "0"}}) 664 sm := NewStoreManager(b) 665 defer func() { 666 c.Check(sm.Stop(), gc.ErrorMatches, "some error") 667 }() 668 w := &Watcher{all: sm} 669 // Receive one delta to make sure that the storeManager 670 // has seen the initial state. 671 checkNext(c, w, []params.Delta{{Entity: &MachineInfo{Id: "0"}}}, "") 672 c.Logf("setting fetch error") 673 b.setFetchError(errors.New("some error")) 674 c.Logf("updating entity") 675 b.updateEntity(&MachineInfo{Id: "1"}) 676 checkNext(c, w, nil, "some error") 677 } 678 679 func StoreIncRef(a *Store, id InfoId) { 680 entry := a.entities[id].Value.(*entityEntry) 681 entry.refCount++ 682 } 683 684 func assertStoreContents(c *gc.C, a *Store, latestRevno int64, entries []entityEntry) { 685 var gotEntries []entityEntry 686 var gotElems []*list.Element 687 c.Check(a.list.Len(), gc.Equals, len(entries)) 688 for e := a.list.Back(); e != nil; e = e.Prev() { 689 gotEntries = append(gotEntries, *e.Value.(*entityEntry)) 690 gotElems = append(gotElems, e) 691 } 692 c.Assert(gotEntries, gc.DeepEquals, entries) 693 for i, ent := range entries { 694 c.Assert(a.entities[ent.info.EntityId()], gc.Equals, gotElems[i]) 695 } 696 c.Assert(a.entities, gc.HasLen, len(entries)) 697 c.Assert(a.latestRevno, gc.Equals, latestRevno) 698 } 699 700 var errTimeout = errors.New("no change received in sufficient time") 701 702 func getNext(c *gc.C, w *Watcher, timeout time.Duration) ([]params.Delta, error) { 703 var deltas []params.Delta 704 var err error 705 ch := make(chan struct{}, 1) 706 go func() { 707 deltas, err = w.Next() 708 ch <- struct{}{} 709 }() 710 select { 711 case <-ch: 712 return deltas, err 713 case <-time.After(1 * time.Second): 714 } 715 return nil, errTimeout 716 } 717 718 func checkNext(c *gc.C, w *Watcher, deltas []params.Delta, expectErr string) { 719 d, err := getNext(c, w, 1*time.Second) 720 if expectErr != "" { 721 c.Check(err, gc.ErrorMatches, expectErr) 722 return 723 } 724 checkDeltasEqual(c, d, deltas) 725 } 726 727 // deltas are returns in arbitrary order, so we compare 728 // them as sets. 729 func checkDeltasEqual(c *gc.C, d0, d1 []params.Delta) { 730 c.Check(deltaMap(d0), gc.DeepEquals, deltaMap(d1)) 731 } 732 733 func deltaMap(deltas []params.Delta) map[InfoId]params.EntityInfo { 734 m := make(map[InfoId]params.EntityInfo) 735 for _, d := range deltas { 736 id := d.Entity.EntityId() 737 if _, ok := m[id]; ok { 738 panic(fmt.Errorf("%v mentioned twice in delta set", id)) 739 } 740 if d.Removed { 741 m[id] = nil 742 } else { 743 m[id] = d.Entity 744 } 745 } 746 return m 747 } 748 749 // watcherState represents a Watcher client's 750 // current view of the state. It holds the last delta that a given 751 // state watcher has seen for each entity. 752 type watcherState map[InfoId]params.Delta 753 754 func (s watcherState) update(changes []params.Delta) { 755 for _, d := range changes { 756 id := d.Entity.EntityId() 757 if d.Removed { 758 if _, ok := s[id]; !ok { 759 panic(fmt.Errorf("entity id %v removed when it wasn't there", id)) 760 } 761 delete(s, id) 762 } else { 763 s[id] = d 764 } 765 } 766 } 767 768 // check checks that the watcher state matches that 769 // held in current. 770 func (s watcherState) check(c *gc.C, current *Store) { 771 currentEntities := make(watcherState) 772 for id, elem := range current.entities { 773 entry := elem.Value.(*entityEntry) 774 if !entry.removed { 775 currentEntities[id] = params.Delta{Entity: entry.info} 776 } 777 } 778 c.Assert(s, gc.DeepEquals, currentEntities) 779 } 780 781 func assertNotReplied(c *gc.C, req *request) { 782 select { 783 case v := <-req.reply: 784 c.Fatalf("request was unexpectedly replied to (got %v)", v) 785 default: 786 } 787 } 788 789 func assertReplied(c *gc.C, val bool, req *request) { 790 select { 791 case v := <-req.reply: 792 c.Assert(v, gc.Equals, val) 793 default: 794 c.Fatalf("request was not replied to") 795 } 796 } 797 798 func assertWaitingRequests(c *gc.C, sm *StoreManager, waiting map[*Watcher][]*request) { 799 c.Assert(sm.waiting, gc.HasLen, len(waiting)) 800 for w, reqs := range waiting { 801 i := 0 802 for req := sm.waiting[w]; ; req = req.next { 803 if i >= len(reqs) { 804 c.Assert(req, gc.IsNil) 805 break 806 } 807 c.Assert(req, gc.Equals, reqs[i]) 808 assertNotReplied(c, req) 809 i++ 810 } 811 } 812 } 813 814 type storeManagerTestBacking struct { 815 mu sync.Mutex 816 fetchErr error 817 entities map[InfoId]params.EntityInfo 818 watchc chan<- watcher.Change 819 txnRevno int64 820 } 821 822 func newTestBacking(initial []params.EntityInfo) *storeManagerTestBacking { 823 b := &storeManagerTestBacking{ 824 entities: make(map[InfoId]params.EntityInfo), 825 } 826 for _, info := range initial { 827 b.entities[info.EntityId()] = info 828 } 829 return b 830 } 831 832 func (b *storeManagerTestBacking) Changed(all *Store, change watcher.Change) error { 833 id := params.EntityId{ 834 Kind: change.C, 835 Id: change.Id.(string), 836 } 837 info, err := b.fetch(id) 838 if err == mgo.ErrNotFound { 839 all.Remove(id) 840 return nil 841 } 842 if err != nil { 843 return err 844 } 845 all.Update(info) 846 return nil 847 } 848 849 func (b *storeManagerTestBacking) fetch(id InfoId) (params.EntityInfo, error) { 850 b.mu.Lock() 851 defer b.mu.Unlock() 852 if b.fetchErr != nil { 853 return nil, b.fetchErr 854 } 855 if info, ok := b.entities[id]; ok { 856 return info, nil 857 } 858 return nil, mgo.ErrNotFound 859 } 860 861 func (b *storeManagerTestBacking) Watch(c chan<- watcher.Change) { 862 b.mu.Lock() 863 defer b.mu.Unlock() 864 if b.watchc != nil { 865 panic("test backing can only watch once") 866 } 867 b.watchc = c 868 } 869 870 func (b *storeManagerTestBacking) Unwatch(c chan<- watcher.Change) { 871 b.mu.Lock() 872 defer b.mu.Unlock() 873 if c != b.watchc { 874 panic("unwatching wrong channel") 875 } 876 b.watchc = nil 877 } 878 879 func (b *storeManagerTestBacking) GetAll(all *Store) error { 880 b.mu.Lock() 881 defer b.mu.Unlock() 882 for _, info := range b.entities { 883 all.Update(info) 884 } 885 return nil 886 } 887 888 func (b *storeManagerTestBacking) updateEntity(info params.EntityInfo) { 889 b.mu.Lock() 890 defer b.mu.Unlock() 891 id := info.EntityId() 892 b.entities[id] = info 893 b.txnRevno++ 894 if b.watchc != nil { 895 b.watchc <- watcher.Change{ 896 C: id.Kind, 897 Id: id.Id, 898 Revno: b.txnRevno, // This is actually ignored, but fill it in anyway. 899 } 900 } 901 } 902 903 func (b *storeManagerTestBacking) setFetchError(err error) { 904 b.mu.Lock() 905 defer b.mu.Unlock() 906 b.fetchErr = err 907 } 908 909 func (b *storeManagerTestBacking) deleteEntity(id0 InfoId) { 910 id := id0.(params.EntityId) 911 b.mu.Lock() 912 defer b.mu.Unlock() 913 delete(b.entities, id) 914 b.txnRevno++ 915 if b.watchc != nil { 916 b.watchc <- watcher.Change{ 917 C: id.Kind, 918 Id: id.Id, 919 Revno: -1, 920 } 921 } 922 } 923 924 type MachineInfo struct { 925 Id string 926 InstanceId string 927 } 928 929 func (i *MachineInfo) EntityId() params.EntityId { 930 return params.EntityId{ 931 Kind: "machine", 932 Id: i.Id, 933 } 934 } 935 936 type ServiceInfo struct { 937 Name string 938 Exposed bool 939 } 940 941 func (i *ServiceInfo) EntityId() params.EntityId { 942 return params.EntityId{ 943 Kind: "service", 944 Id: i.Name, 945 } 946 }