github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/watcher/watcher_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package watcher_test
     5  
     6  import (
     7  	stdtesting "testing"
     8  	"time"
     9  
    10  	"labix.org/v2/mgo"
    11  	"labix.org/v2/mgo/txn"
    12  	gc "launchpad.net/gocheck"
    13  	"launchpad.net/tomb"
    14  
    15  	"github.com/juju/juju/state/watcher"
    16  	"github.com/juju/juju/testing"
    17  )
    18  
    19  // Test tuning parameters.
    20  const (
    21  	// worstCase is used for timeouts when timing out
    22  	// will fail the test. Raising this value should
    23  	// not affect the overall running time of the tests
    24  	// unless they fail.
    25  	worstCase = testing.LongWait
    26  
    27  	// justLongEnough is used for timeouts that
    28  	// are expected to happen for a test to complete
    29  	// successfully. Reducing this value will make
    30  	// the tests run faster at the expense of making them
    31  	// fail more often on heavily loaded or slow hardware.
    32  	justLongEnough = testing.ShortWait
    33  
    34  	// fastPeriod specifies the period of the watcher for
    35  	// tests where the timing is not critical.
    36  	fastPeriod = 10 * time.Millisecond
    37  
    38  	// slowPeriod specifies the period of the watcher
    39  	// for tests where the timing is important.
    40  	slowPeriod = 1 * time.Second
    41  )
    42  
    43  func TestPackage(t *stdtesting.T) {
    44  	testing.MgoTestPackage(t)
    45  }
    46  
    47  type watcherSuite struct {
    48  	testing.MgoSuite
    49  	testing.BaseSuite
    50  
    51  	log       *mgo.Collection
    52  	stash     *mgo.Collection
    53  	runner    *txn.Runner
    54  	w         *watcher.Watcher
    55  	ch        chan watcher.Change
    56  	oldPeriod time.Duration
    57  }
    58  
    59  // FastPeriodSuite implements tests that should
    60  // work regardless of the watcher refresh period.
    61  type FastPeriodSuite struct {
    62  	watcherSuite
    63  }
    64  
    65  func (s *FastPeriodSuite) SetUpSuite(c *gc.C) {
    66  	s.watcherSuite.SetUpSuite(c)
    67  	watcher.Period = fastPeriod
    68  }
    69  
    70  var _ = gc.Suite(&FastPeriodSuite{})
    71  
    72  func (s *watcherSuite) SetUpSuite(c *gc.C) {
    73  	s.BaseSuite.SetUpSuite(c)
    74  	s.MgoSuite.SetUpSuite(c)
    75  	s.oldPeriod = watcher.Period
    76  }
    77  
    78  func (s *watcherSuite) TearDownSuite(c *gc.C) {
    79  	s.MgoSuite.TearDownSuite(c)
    80  	s.BaseSuite.TearDownSuite(c)
    81  	watcher.Period = s.oldPeriod
    82  }
    83  
    84  func (s *watcherSuite) SetUpTest(c *gc.C) {
    85  	s.BaseSuite.SetUpTest(c)
    86  	s.MgoSuite.SetUpTest(c)
    87  
    88  	db := s.MgoSuite.Session.DB("juju")
    89  	s.log = db.C("txnlog")
    90  	s.log.Create(&mgo.CollectionInfo{
    91  		Capped:   true,
    92  		MaxBytes: 1000000,
    93  	})
    94  	s.stash = db.C("txn.stash")
    95  	s.runner = txn.NewRunner(db.C("txn"))
    96  	s.runner.ChangeLog(s.log)
    97  	s.w = watcher.New(s.log)
    98  	s.ch = make(chan watcher.Change)
    99  }
   100  
   101  func (s *watcherSuite) TearDownTest(c *gc.C) {
   102  	c.Assert(s.w.Stop(), gc.IsNil)
   103  
   104  	s.MgoSuite.TearDownTest(c)
   105  	s.BaseSuite.TearDownTest(c)
   106  }
   107  
   108  type M map[string]interface{}
   109  
   110  func assertChange(c *gc.C, watch <-chan watcher.Change, want watcher.Change) {
   111  	select {
   112  	case got := <-watch:
   113  		if got != want {
   114  			c.Fatalf("watch reported %v, want %v", got, want)
   115  		}
   116  	case <-time.After(worstCase):
   117  		c.Fatalf("watch reported nothing, want %v", want)
   118  	}
   119  }
   120  
   121  func assertNoChange(c *gc.C, watch <-chan watcher.Change) {
   122  	select {
   123  	case got := <-watch:
   124  		c.Fatalf("watch reported %v, want nothing", got)
   125  	case <-time.After(justLongEnough):
   126  	}
   127  }
   128  
   129  func assertOrder(c *gc.C, revnos ...int64) {
   130  	last := int64(-2)
   131  	for _, revno := range revnos {
   132  		if revno <= last {
   133  			c.Fatalf("got bad revno sequence: %v", revnos)
   134  		}
   135  		last = revno
   136  	}
   137  }
   138  
   139  func (s *watcherSuite) revno(c string, id interface{}) (revno int64) {
   140  	var doc struct {
   141  		Revno int64 "txn-revno"
   142  	}
   143  	err := s.log.Database.C(c).FindId(id).One(&doc)
   144  	if err != nil {
   145  		panic(err)
   146  	}
   147  	return doc.Revno
   148  }
   149  
   150  func (s *watcherSuite) insert(c *gc.C, coll string, id interface{}) (revno int64) {
   151  	ops := []txn.Op{{C: coll, Id: id, Insert: M{"n": 1}}}
   152  	err := s.runner.Run(ops, "", nil)
   153  	if err != nil {
   154  		panic(err)
   155  	}
   156  	revno = s.revno(coll, id)
   157  	c.Logf("insert(%#v, %#v) => revno %d", coll, id, revno)
   158  	return revno
   159  }
   160  
   161  func (s *watcherSuite) insertAll(c *gc.C, coll string, ids ...interface{}) (revnos []int64) {
   162  	var ops []txn.Op
   163  	for _, id := range ids {
   164  		ops = append(ops, txn.Op{C: coll, Id: id, Insert: M{"n": 1}})
   165  	}
   166  	err := s.runner.Run(ops, "", nil)
   167  	if err != nil {
   168  		panic(err)
   169  	}
   170  	for _, id := range ids {
   171  		revnos = append(revnos, s.revno(coll, id))
   172  	}
   173  	c.Logf("insertAll(%#v, %v) => revnos %v", coll, ids, revnos)
   174  	return revnos
   175  }
   176  
   177  func (s *watcherSuite) update(c *gc.C, coll string, id interface{}) (revno int64) {
   178  	ops := []txn.Op{{C: coll, Id: id, Update: M{"$inc": M{"n": 1}}}}
   179  	err := s.runner.Run(ops, "", nil)
   180  	if err != nil {
   181  		panic(err)
   182  	}
   183  	revno = s.revno(coll, id)
   184  	c.Logf("update(%#v, %#v) => revno %d", coll, id, revno)
   185  	return revno
   186  }
   187  
   188  func (s *watcherSuite) remove(c *gc.C, coll string, id interface{}) (revno int64) {
   189  	ops := []txn.Op{{C: coll, Id: id, Remove: true}}
   190  	err := s.runner.Run(ops, "", nil)
   191  	if err != nil {
   192  		panic(err)
   193  	}
   194  	c.Logf("remove(%#v, %#v) => revno -1", coll, id)
   195  	return -1
   196  }
   197  
   198  func (s *FastPeriodSuite) TestErrAndDead(c *gc.C) {
   199  	c.Assert(s.w.Err(), gc.Equals, tomb.ErrStillAlive)
   200  	select {
   201  	case <-s.w.Dead():
   202  		c.Fatalf("Dead channel fired unexpectedly")
   203  	default:
   204  	}
   205  	c.Assert(s.w.Stop(), gc.IsNil)
   206  	c.Assert(s.w.Err(), gc.IsNil)
   207  	select {
   208  	case <-s.w.Dead():
   209  	default:
   210  		c.Fatalf("Dead channel should have fired")
   211  	}
   212  }
   213  
   214  func (s *FastPeriodSuite) TestWatchBeforeKnown(c *gc.C) {
   215  	s.w.Watch("test", "a", -1, s.ch)
   216  	assertNoChange(c, s.ch)
   217  
   218  	revno := s.insert(c, "test", "a")
   219  
   220  	s.w.StartSync()
   221  	assertChange(c, s.ch, watcher.Change{"test", "a", revno})
   222  	assertNoChange(c, s.ch)
   223  
   224  	assertOrder(c, -1, revno)
   225  }
   226  
   227  func (s *FastPeriodSuite) TestWatchAfterKnown(c *gc.C) {
   228  	revno := s.insert(c, "test", "a")
   229  
   230  	s.w.StartSync()
   231  
   232  	s.w.Watch("test", "a", -1, s.ch)
   233  	assertChange(c, s.ch, watcher.Change{"test", "a", revno})
   234  	assertNoChange(c, s.ch)
   235  
   236  	assertOrder(c, -1, revno)
   237  }
   238  
   239  func (s *FastPeriodSuite) TestWatchIgnoreUnwatched(c *gc.C) {
   240  	s.w.Watch("test", "a", -1, s.ch)
   241  	assertNoChange(c, s.ch)
   242  
   243  	s.insert(c, "test", "b")
   244  
   245  	s.w.StartSync()
   246  	assertNoChange(c, s.ch)
   247  }
   248  
   249  func (s *FastPeriodSuite) TestWatchOrder(c *gc.C) {
   250  	s.w.StartSync()
   251  	for _, id := range []string{"a", "b", "c", "d"} {
   252  		s.w.Watch("test", id, -1, s.ch)
   253  	}
   254  	revno1 := s.insert(c, "test", "a")
   255  	revno2 := s.insert(c, "test", "b")
   256  	revno3 := s.insert(c, "test", "c")
   257  
   258  	s.w.StartSync()
   259  	assertChange(c, s.ch, watcher.Change{"test", "a", revno1})
   260  	assertChange(c, s.ch, watcher.Change{"test", "b", revno2})
   261  	assertChange(c, s.ch, watcher.Change{"test", "c", revno3})
   262  	assertNoChange(c, s.ch)
   263  }
   264  
   265  func (s *FastPeriodSuite) TestTransactionWithMultiple(c *gc.C) {
   266  	s.w.StartSync()
   267  	for _, id := range []string{"a", "b", "c"} {
   268  		s.w.Watch("test", id, -1, s.ch)
   269  	}
   270  	revnos := s.insertAll(c, "test", "a", "b", "c")
   271  	s.w.StartSync()
   272  	assertChange(c, s.ch, watcher.Change{"test", "a", revnos[0]})
   273  	assertChange(c, s.ch, watcher.Change{"test", "b", revnos[1]})
   274  	assertChange(c, s.ch, watcher.Change{"test", "c", revnos[2]})
   275  	assertNoChange(c, s.ch)
   276  }
   277  
   278  func (s *FastPeriodSuite) TestWatchMultipleChannels(c *gc.C) {
   279  	ch1 := make(chan watcher.Change)
   280  	ch2 := make(chan watcher.Change)
   281  	ch3 := make(chan watcher.Change)
   282  	s.w.Watch("test1", 1, -1, ch1)
   283  	s.w.Watch("test2", 2, -1, ch2)
   284  	s.w.Watch("test3", 3, -1, ch3)
   285  	revno1 := s.insert(c, "test1", 1)
   286  	revno2 := s.insert(c, "test2", 2)
   287  	revno3 := s.insert(c, "test3", 3)
   288  	s.w.StartSync()
   289  	s.w.Unwatch("test2", 2, ch2)
   290  	assertChange(c, ch1, watcher.Change{"test1", 1, revno1})
   291  	_ = revno2
   292  	assertChange(c, ch3, watcher.Change{"test3", 3, revno3})
   293  	assertNoChange(c, ch1)
   294  	assertNoChange(c, ch2)
   295  	assertNoChange(c, ch3)
   296  }
   297  
   298  func (s *FastPeriodSuite) TestIgnoreAncientHistory(c *gc.C) {
   299  	s.insert(c, "test", "a")
   300  
   301  	w := watcher.New(s.log)
   302  	defer w.Stop()
   303  	w.StartSync()
   304  
   305  	w.Watch("test", "a", -1, s.ch)
   306  	assertNoChange(c, s.ch)
   307  }
   308  
   309  func (s *FastPeriodSuite) TestUpdate(c *gc.C) {
   310  	s.w.Watch("test", "a", -1, s.ch)
   311  	assertNoChange(c, s.ch)
   312  
   313  	revno1 := s.insert(c, "test", "a")
   314  	s.w.StartSync()
   315  	assertChange(c, s.ch, watcher.Change{"test", "a", revno1})
   316  	assertNoChange(c, s.ch)
   317  
   318  	revno2 := s.update(c, "test", "a")
   319  	s.w.StartSync()
   320  	assertChange(c, s.ch, watcher.Change{"test", "a", revno2})
   321  
   322  	assertOrder(c, -1, revno1, revno2)
   323  }
   324  
   325  func (s *FastPeriodSuite) TestRemove(c *gc.C) {
   326  	s.w.Watch("test", "a", -1, s.ch)
   327  	assertNoChange(c, s.ch)
   328  
   329  	revno1 := s.insert(c, "test", "a")
   330  	s.w.StartSync()
   331  	assertChange(c, s.ch, watcher.Change{"test", "a", revno1})
   332  	assertNoChange(c, s.ch)
   333  
   334  	revno2 := s.remove(c, "test", "a")
   335  	s.w.StartSync()
   336  	assertChange(c, s.ch, watcher.Change{"test", "a", -1})
   337  	assertNoChange(c, s.ch)
   338  
   339  	revno3 := s.insert(c, "test", "a")
   340  	s.w.StartSync()
   341  	assertChange(c, s.ch, watcher.Change{"test", "a", revno3})
   342  	assertNoChange(c, s.ch)
   343  
   344  	assertOrder(c, revno2, revno1)
   345  	assertOrder(c, revno2, revno3)
   346  }
   347  
   348  func (s *FastPeriodSuite) TestWatchKnownRemove(c *gc.C) {
   349  	revno1 := s.insert(c, "test", "a")
   350  	revno2 := s.remove(c, "test", "a")
   351  	s.w.StartSync()
   352  
   353  	s.w.Watch("test", "a", revno1, s.ch)
   354  	assertChange(c, s.ch, watcher.Change{"test", "a", revno2})
   355  
   356  	assertOrder(c, revno2, revno1)
   357  }
   358  
   359  func (s *FastPeriodSuite) TestScale(c *gc.C) {
   360  	const N = 500
   361  	const T = 10
   362  
   363  	c.Logf("Creating %d documents, %d per transaction...", N, T)
   364  	ops := make([]txn.Op, T)
   365  	for i := 0; i < (N / T); i++ {
   366  		ops = ops[:0]
   367  		for j := 0; j < T && i*T+j < N; j++ {
   368  			ops = append(ops, txn.Op{C: "test", Id: i*T + j, Insert: M{"n": 1}})
   369  		}
   370  		err := s.runner.Run(ops, "", nil)
   371  		c.Assert(err, gc.IsNil)
   372  	}
   373  
   374  	c.Logf("Watching all documents...")
   375  	for i := 0; i < N; i++ {
   376  		s.w.Watch("test", i, -1, s.ch)
   377  	}
   378  
   379  	c.Logf("Forcing a refresh...")
   380  	s.w.StartSync()
   381  
   382  	count, err := s.Session.DB("juju").C("test").Count()
   383  	c.Assert(err, gc.IsNil)
   384  	c.Logf("Got %d documents in the collection...", count)
   385  	c.Assert(count, gc.Equals, N)
   386  
   387  	c.Logf("Reading all changes...")
   388  	seen := make(map[interface{}]bool)
   389  	for i := 0; i < N; i++ {
   390  		select {
   391  		case change := <-s.ch:
   392  			seen[change.Id] = true
   393  		case <-time.After(worstCase):
   394  			c.Fatalf("not enough changes: got %d, want %d", len(seen), N)
   395  		}
   396  	}
   397  	c.Assert(len(seen), gc.Equals, N)
   398  }
   399  
   400  func (s *FastPeriodSuite) TestWatchUnwatchOnQueue(c *gc.C) {
   401  	const N = 10
   402  	for i := 0; i < N; i++ {
   403  		s.insert(c, "test", i)
   404  	}
   405  	s.w.StartSync()
   406  	for i := 0; i < N; i++ {
   407  		s.w.Watch("test", i, -1, s.ch)
   408  	}
   409  	for i := 1; i < N; i += 2 {
   410  		s.w.Unwatch("test", i, s.ch)
   411  	}
   412  	s.w.StartSync()
   413  	seen := make(map[interface{}]bool)
   414  	for i := 0; i < N/2; i++ {
   415  		select {
   416  		case change := <-s.ch:
   417  			seen[change.Id] = true
   418  		case <-time.After(worstCase):
   419  			c.Fatalf("not enough changes: got %d, want %d", len(seen), N/2)
   420  		}
   421  	}
   422  	c.Assert(len(seen), gc.Equals, N/2)
   423  	assertNoChange(c, s.ch)
   424  }
   425  
   426  func (s *FastPeriodSuite) TestStartSync(c *gc.C) {
   427  	s.w.Watch("test", "a", -1, s.ch)
   428  
   429  	revno := s.insert(c, "test", "a")
   430  
   431  	done := make(chan bool)
   432  	go func() {
   433  		s.w.StartSync()
   434  		s.w.StartSync()
   435  		s.w.StartSync()
   436  		done <- true
   437  	}()
   438  
   439  	select {
   440  	case <-done:
   441  	case <-time.After(worstCase):
   442  		c.Fatalf("StartSync failed to return")
   443  	}
   444  
   445  	assertChange(c, s.ch, watcher.Change{"test", "a", revno})
   446  }
   447  
   448  func (s *FastPeriodSuite) TestWatchCollection(c *gc.C) {
   449  	chA1 := make(chan watcher.Change)
   450  	chB1 := make(chan watcher.Change)
   451  	chA := make(chan watcher.Change)
   452  	chB := make(chan watcher.Change)
   453  
   454  	s.w.Watch("testA", 1, -1, chA1)
   455  	s.w.Watch("testB", 1, -1, chB1)
   456  	s.w.WatchCollection("testA", chA)
   457  	s.w.WatchCollection("testB", chB)
   458  
   459  	revno1 := s.insert(c, "testA", 1)
   460  	revno2 := s.insert(c, "testA", 2)
   461  	revno3 := s.insert(c, "testB", 1)
   462  	revno4 := s.insert(c, "testB", 2)
   463  
   464  	s.w.StartSync()
   465  
   466  	seen := map[chan<- watcher.Change][]watcher.Change{}
   467  	timeout := time.After(testing.LongWait)
   468  	n := 0
   469  Loop1:
   470  	for n < 6 {
   471  		select {
   472  		case chg := <-chA1:
   473  			seen[chA1] = append(seen[chA1], chg)
   474  		case chg := <-chB1:
   475  			seen[chB1] = append(seen[chB1], chg)
   476  		case chg := <-chA:
   477  			seen[chA] = append(seen[chA], chg)
   478  		case chg := <-chB:
   479  			seen[chB] = append(seen[chB], chg)
   480  		case <-timeout:
   481  			break Loop1
   482  		}
   483  		n++
   484  	}
   485  
   486  	c.Check(seen[chA1], gc.DeepEquals, []watcher.Change{{"testA", 1, revno1}})
   487  	c.Check(seen[chB1], gc.DeepEquals, []watcher.Change{{"testB", 1, revno3}})
   488  	c.Check(seen[chA], gc.DeepEquals, []watcher.Change{{"testA", 1, revno1}, {"testA", 2, revno2}})
   489  	c.Check(seen[chB], gc.DeepEquals, []watcher.Change{{"testB", 1, revno3}, {"testB", 2, revno4}})
   490  	if c.Failed() {
   491  		return
   492  	}
   493  
   494  	s.w.UnwatchCollection("testB", chB)
   495  	s.w.Unwatch("testB", 1, chB1)
   496  
   497  	revno1 = s.update(c, "testA", 1)
   498  	revno3 = s.update(c, "testB", 1)
   499  
   500  	s.w.StartSync()
   501  
   502  	timeout = time.After(testing.LongWait)
   503  	seen = map[chan<- watcher.Change][]watcher.Change{}
   504  	n = 0
   505  Loop2:
   506  	for n < 2 {
   507  		select {
   508  		case chg := <-chA1:
   509  			seen[chA1] = append(seen[chA1], chg)
   510  		case chg := <-chB1:
   511  			seen[chB1] = append(seen[chB1], chg)
   512  		case chg := <-chA:
   513  			seen[chA] = append(seen[chA], chg)
   514  		case chg := <-chB:
   515  			seen[chB] = append(seen[chB], chg)
   516  		case <-timeout:
   517  			break Loop2
   518  		}
   519  		n++
   520  	}
   521  	c.Check(seen[chA1], gc.DeepEquals, []watcher.Change{{"testA", 1, revno1}})
   522  	c.Check(seen[chB1], gc.IsNil)
   523  	c.Check(seen[chA], gc.DeepEquals, []watcher.Change{{"testA", 1, revno1}})
   524  	c.Check(seen[chB], gc.IsNil)
   525  
   526  	// Check that no extra events arrive.
   527  	seen = map[chan<- watcher.Change][]watcher.Change{}
   528  	timeout = time.After(testing.ShortWait)
   529  Loop3:
   530  	for {
   531  		select {
   532  		case chg := <-chA1:
   533  			seen[chA1] = append(seen[chA1], chg)
   534  		case chg := <-chB1:
   535  			seen[chB1] = append(seen[chB1], chg)
   536  		case chg := <-chA:
   537  			seen[chA] = append(seen[chA], chg)
   538  		case chg := <-chB:
   539  			seen[chB] = append(seen[chB], chg)
   540  		case <-timeout:
   541  			break Loop3
   542  		}
   543  	}
   544  	c.Check(seen[chA1], gc.IsNil)
   545  	c.Check(seen[chB1], gc.IsNil)
   546  	c.Check(seen[chA], gc.IsNil)
   547  	c.Check(seen[chB], gc.IsNil)
   548  }
   549  
   550  func (s *FastPeriodSuite) TestUnwatchCollectionWithFilter(c *gc.C) {
   551  	filter := func(key interface{}) bool {
   552  		id := key.(int)
   553  		return id != 2
   554  	}
   555  	chA := make(chan watcher.Change)
   556  	s.w.WatchCollectionWithFilter("testA", chA, filter)
   557  	revnoA := s.insert(c, "testA", 1)
   558  	assertChange(c, chA, watcher.Change{"testA", 1, revnoA})
   559  	s.insert(c, "testA", 2)
   560  	assertNoChange(c, chA)
   561  	s.insert(c, "testA", 3)
   562  	s.w.StartSync()
   563  	assertChange(c, chA, watcher.Change{"testA", 3, revnoA})
   564  }
   565  
   566  func (s *FastPeriodSuite) TestUnwatchCollectionWithOutstandingRequest(c *gc.C) {
   567  	chA := make(chan watcher.Change)
   568  	s.w.WatchCollection("testA", chA)
   569  	chB := make(chan watcher.Change)
   570  	s.w.Watch("testB", 1, -1, chB)
   571  	revnoA := s.insert(c, "testA", 1)
   572  	s.insert(c, "testA", 2)
   573  	// By inserting this *after* the testA document, we ensure that
   574  	// the watcher will try to send on chB after sending on chA.
   575  	// The original bug that this test guards against meant that the
   576  	// UnwatchCollection did not correctly cancel the outstanding
   577  	// request, so the loop would never get around to sending on
   578  	// chB.
   579  	revnoB := s.insert(c, "testB", 1)
   580  	s.w.StartSync()
   581  	// When we receive the first change on chA, we know that
   582  	// the watcher is trying to send changes on all the
   583  	// watcher channels (2 changes on chA and 1 change on chB).
   584  	assertChange(c, chA, watcher.Change{"testA", 1, revnoA})
   585  	s.w.UnwatchCollection("testA", chA)
   586  	assertChange(c, chB, watcher.Change{"testB", 1, revnoB})
   587  }
   588  
   589  func (s *FastPeriodSuite) TestNonMutatingTxn(c *gc.C) {
   590  	chA1 := make(chan watcher.Change)
   591  	chA := make(chan watcher.Change)
   592  
   593  	revno1 := s.insert(c, "test", "a")
   594  
   595  	s.w.StartSync()
   596  
   597  	s.w.Watch("test", 1, revno1, chA1)
   598  	s.w.WatchCollection("test", chA)
   599  
   600  	revno2 := s.insert(c, "test", "a")
   601  
   602  	c.Assert(revno1, gc.Equals, revno2)
   603  
   604  	s.w.StartSync()
   605  
   606  	assertNoChange(c, chA1)
   607  	assertNoChange(c, chA)
   608  }
   609  
   610  // SlowPeriodSuite implements tests
   611  // that are flaky when the watcher refresh period
   612  // is small.
   613  type SlowPeriodSuite struct {
   614  	watcherSuite
   615  }
   616  
   617  func (s *SlowPeriodSuite) SetUpSuite(c *gc.C) {
   618  	s.watcherSuite.SetUpSuite(c)
   619  	watcher.Period = slowPeriod
   620  }
   621  
   622  var _ = gc.Suite(&SlowPeriodSuite{})
   623  
   624  func (s *SlowPeriodSuite) TestWatchBeforeRemoveKnown(c *gc.C) {
   625  	revno1 := s.insert(c, "test", "a")
   626  	s.w.StartSync()
   627  	revno2 := s.remove(c, "test", "a")
   628  
   629  	s.w.Watch("test", "a", -1, s.ch)
   630  	assertChange(c, s.ch, watcher.Change{"test", "a", revno1})
   631  	s.w.StartSync()
   632  	assertChange(c, s.ch, watcher.Change{"test", "a", revno2})
   633  
   634  	assertOrder(c, revno2, revno1)
   635  }
   636  
   637  func (s *SlowPeriodSuite) TestDoubleUpdate(c *gc.C) {
   638  	assertNoChange(c, s.ch)
   639  
   640  	revno1 := s.insert(c, "test", "a")
   641  	s.w.StartSync()
   642  
   643  	revno2 := s.update(c, "test", "a")
   644  	revno3 := s.update(c, "test", "a")
   645  
   646  	s.w.Watch("test", "a", revno2, s.ch)
   647  	assertNoChange(c, s.ch)
   648  
   649  	s.w.StartSync()
   650  	assertChange(c, s.ch, watcher.Change{"test", "a", revno3})
   651  	assertNoChange(c, s.ch)
   652  
   653  	assertOrder(c, -1, revno1, revno2, revno3)
   654  }
   655  
   656  func (s *SlowPeriodSuite) TestWatchPeriod(c *gc.C) {
   657  	revno1 := s.insert(c, "test", "a")
   658  	s.w.StartSync()
   659  	t0 := time.Now()
   660  	s.w.Watch("test", "a", revno1, s.ch)
   661  	revno2 := s.update(c, "test", "a")
   662  
   663  	leeway := watcher.Period / 4
   664  	select {
   665  	case got := <-s.ch:
   666  		gotPeriod := time.Since(t0)
   667  		c.Assert(got, gc.Equals, watcher.Change{"test", "a", revno2})
   668  		if gotPeriod < watcher.Period-leeway {
   669  			c.Fatalf("watcher not waiting long enough; got %v want %v", gotPeriod, watcher.Period)
   670  		}
   671  	case <-time.After(watcher.Period + leeway):
   672  		gotPeriod := time.Since(t0)
   673  		c.Fatalf("watcher waited too long; got %v want %v", gotPeriod, watcher.Period)
   674  	}
   675  
   676  	assertOrder(c, -1, revno1, revno2)
   677  }
   678  
   679  func (s *SlowPeriodSuite) TestStartSyncStartsImmediately(c *gc.C) {
   680  	// Ensure we're at the start of a sync cycle.
   681  	s.w.StartSync()
   682  	time.Sleep(justLongEnough)
   683  
   684  	// Watching after StartSync should see the current state of affairs.
   685  	revno := s.insert(c, "test", "a")
   686  	s.w.StartSync()
   687  	s.w.Watch("test", "a", -1, s.ch)
   688  	select {
   689  	case got := <-s.ch:
   690  		c.Assert(got.Revno, gc.Equals, revno)
   691  	case <-time.After(watcher.Period / 2):
   692  		c.Fatalf("watch after StartSync is still using old info")
   693  	}
   694  
   695  	s.remove(c, "test", "a")
   696  	s.w.StartSync()
   697  	ch := make(chan watcher.Change)
   698  	s.w.Watch("test", "a", -1, ch)
   699  	select {
   700  	case got := <-ch:
   701  		c.Fatalf("got event %#v when starting watcher after doc was removed", got)
   702  	case <-time.After(justLongEnough):
   703  	}
   704  }