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