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  }