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