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