github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/state/multiwatcher/multiwatcher_internal_test.go (about)

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