github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/watcher/hubwatcher_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package watcher_test
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/clock"
    10  	"github.com/juju/loggo"
    11  	"github.com/juju/pubsub"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/juju/worker.v1"
    15  	"gopkg.in/tomb.v2"
    16  
    17  	"github.com/juju/juju/state/watcher"
    18  	"github.com/juju/juju/testing"
    19  )
    20  
    21  type HubWatcherSuite struct {
    22  	testing.BaseSuite
    23  
    24  	w   watcher.BaseWatcher
    25  	hub *pubsub.SimpleHub
    26  	ch  chan watcher.Change
    27  }
    28  
    29  var _ = gc.Suite(&HubWatcherSuite{})
    30  
    31  func (s *HubWatcherSuite) SetUpTest(c *gc.C) {
    32  	s.BaseSuite.SetUpTest(c)
    33  
    34  	logger := loggo.GetLogger("HubWatcherSuite")
    35  	logger.SetLogLevel(loggo.TRACE)
    36  
    37  	s.hub = pubsub.NewSimpleHub(nil)
    38  	s.ch = make(chan watcher.Change)
    39  	var started <-chan struct{}
    40  	s.w, started = watcher.NewTestHubWatcher(s.hub, clock.WallClock, "model-uuid", logger)
    41  	s.AddCleanup(func(c *gc.C) {
    42  		worker.Stop(s.w)
    43  	})
    44  	select {
    45  	case <-started:
    46  		// all good
    47  	case <-time.After(testing.LongWait):
    48  		c.Error("hub watcher worker didn't start")
    49  	}
    50  }
    51  
    52  func (s *HubWatcherSuite) publish(c *gc.C, changes ...watcher.Change) {
    53  	var processed <-chan struct{}
    54  	for _, change := range changes {
    55  		processed = s.hub.Publish(watcher.TxnWatcherCollection, change)
    56  	}
    57  	select {
    58  	case <-processed:
    59  		// all good.
    60  	case <-time.After(testing.LongWait):
    61  		c.Error("event not processed")
    62  	}
    63  
    64  }
    65  
    66  func (s *HubWatcherSuite) TestErrAndDead(c *gc.C) {
    67  	c.Assert(s.w.Err(), gc.Equals, tomb.ErrStillAlive)
    68  	select {
    69  	case <-s.w.Dead():
    70  		c.Fatalf("Dead channel fired unexpectedly")
    71  	default:
    72  	}
    73  	c.Assert(worker.Stop(s.w), jc.ErrorIsNil)
    74  	select {
    75  	case <-s.w.Dead():
    76  	default:
    77  		c.Fatalf("Dead channel should have fired")
    78  	}
    79  }
    80  
    81  func (s *HubWatcherSuite) TestTxnWatcherSyncErrWorker(c *gc.C) {
    82  	// When the TxnWatcher hits a sync error and restarts, the hub watcher needs
    83  	// to restart too as there may be missed events, so all the watches this hub
    84  	// has need to be invalidated. This happens by the worker dying.
    85  	s.hub.Publish(watcher.TxnWatcherSyncErr, nil)
    86  
    87  	select {
    88  	case <-s.w.Dead():
    89  	case <-time.After(testing.LongWait):
    90  		c.Fatalf("Dead channel should have fired")
    91  	}
    92  
    93  	c.Assert(s.w.Err(), gc.ErrorMatches, "txn watcher sync error")
    94  }
    95  
    96  func (s *HubWatcherSuite) TestWatchBeforeKnown(c *gc.C) {
    97  	s.w.Watch("test", "a", s.ch)
    98  	assertNoChange(c, s.ch)
    99  
   100  	change := watcher.Change{"test", "a", 5}
   101  	s.publish(c, change)
   102  
   103  	assertChange(c, s.ch, change)
   104  	assertNoChange(c, s.ch)
   105  }
   106  
   107  func (s *HubWatcherSuite) TestWatchAfterKnown(c *gc.C) {
   108  	change := watcher.Change{"test", "a", 5}
   109  	s.publish(c, change)
   110  
   111  	// Watch doesn't publish an initial event, whether or not we've
   112  	// seen the document before.
   113  	s.w.Watch("test", "a", s.ch)
   114  	assertNoChange(c, s.ch)
   115  }
   116  
   117  func (s *HubWatcherSuite) TestWatchIgnoreUnwatched(c *gc.C) {
   118  	s.w.Watch("test", "a", s.ch)
   119  
   120  	s.publish(c, watcher.Change{"test", "b", 5})
   121  
   122  	assertNoChange(c, s.ch)
   123  }
   124  
   125  func (s *HubWatcherSuite) TestWatchMultiBeforeKnown(c *gc.C) {
   126  	err := s.w.WatchMulti("test", []interface{}{"a", "b"}, s.ch)
   127  	c.Assert(err, jc.ErrorIsNil)
   128  	assertNoChange(c, s.ch)
   129  
   130  	change := watcher.Change{"test", "a", 5}
   131  	s.publish(c, change)
   132  
   133  	assertChange(c, s.ch, change)
   134  	assertNoChange(c, s.ch)
   135  }
   136  
   137  func (s *HubWatcherSuite) TestWatchMultiDuplicateWatch(c *gc.C) {
   138  	s.w.Watch("test", "b", s.ch)
   139  	assertNoChange(c, s.ch)
   140  	err := s.w.WatchMulti("test", []interface{}{"a", "b"}, s.ch)
   141  	c.Assert(err, gc.ErrorMatches, `tried to re-add channel .* for document "b" in collection "test"`)
   142  	// Changes to "a" should not be watched as we had an error
   143  	s.publish(c, watcher.Change{"test", "a", 5})
   144  	assertNoChange(c, s.ch)
   145  }
   146  
   147  func (s *HubWatcherSuite) TestWatchMultiInvalidId(c *gc.C) {
   148  	err := s.w.WatchMulti("test", []interface{}{"a", nil}, s.ch)
   149  	c.Assert(err, gc.ErrorMatches, `cannot watch a document with nil id`)
   150  	// Changes to "a" should not be watched as we had an error
   151  	s.publish(c, watcher.Change{"test", "a", 5})
   152  	assertNoChange(c, s.ch)
   153  }
   154  
   155  func (s *HubWatcherSuite) TestWatchMultiAfterKnown(c *gc.C) {
   156  	s.publish(c, watcher.Change{"test", "a", 5})
   157  	err := s.w.WatchMulti("test", []interface{}{"a", "b"}, s.ch)
   158  	c.Assert(err, jc.ErrorIsNil)
   159  	assertNoChange(c, s.ch)
   160  	// We don't see the change that occurred before we started watching, but we see any changes after that fact
   161  	change := watcher.Change{"test", "a", 6}
   162  	s.publish(c, change)
   163  	assertChange(c, s.ch, change)
   164  	assertNoChange(c, s.ch)
   165  }
   166  
   167  func (s *HubWatcherSuite) TestWatchOrder(c *gc.C) {
   168  	first := watcher.Change{"test", "a", 3}
   169  	second := watcher.Change{"test", "b", 4}
   170  	third := watcher.Change{"test", "c", 5}
   171  
   172  	for _, id := range []string{"a", "b", "c", "d"} {
   173  		s.w.Watch("test", id, s.ch)
   174  	}
   175  
   176  	s.publish(c, first, second, third)
   177  
   178  	assertChange(c, s.ch, first)
   179  	assertChange(c, s.ch, second)
   180  	assertChange(c, s.ch, third)
   181  	assertNoChange(c, s.ch)
   182  }
   183  
   184  func (s *HubWatcherSuite) TestWatchMultipleChannels(c *gc.C) {
   185  	ch1 := make(chan watcher.Change)
   186  	ch2 := make(chan watcher.Change)
   187  	ch3 := make(chan watcher.Change)
   188  	s.w.Watch("test1", 1, ch1)
   189  	s.w.Watch("test2", 2, ch2)
   190  	s.w.Watch("test3", 3, ch3)
   191  
   192  	first := watcher.Change{"test1", 1, 3}
   193  	second := watcher.Change{"test2", 2, 4}
   194  	third := watcher.Change{"test3", 3, 5}
   195  	s.publish(c, first, second, third)
   196  
   197  	s.w.Unwatch("test2", 2, ch2)
   198  	assertChange(c, ch1, first)
   199  	assertChange(c, ch3, third)
   200  	assertNoChange(c, ch1)
   201  	assertNoChange(c, ch2)
   202  	assertNoChange(c, ch3)
   203  }
   204  
   205  func (s *HubWatcherSuite) TestWatchAlreadyRemoved(c *gc.C) {
   206  	change := watcher.Change{"test", "a", -1}
   207  	s.publish(c, change)
   208  
   209  	s.w.Watch("test", "a", s.ch)
   210  	assertNoChange(c, s.ch)
   211  }
   212  
   213  func (s *HubWatcherSuite) TestWatchUnwatchOnQueue(c *gc.C) {
   214  	const N = 10
   215  	for i := 0; i < N; i++ {
   216  		s.w.Watch("test", i, s.ch)
   217  	}
   218  	for i := 0; i < N; i++ {
   219  		s.publish(c, watcher.Change{"test", i, int64(i + 3)})
   220  	}
   221  	for i := 1; i < N; i += 2 {
   222  		s.w.Unwatch("test", i, s.ch)
   223  	}
   224  	seen := make(map[interface{}]bool)
   225  	for i := 0; i < N/2; i++ {
   226  		select {
   227  		case change := <-s.ch:
   228  			seen[change.Id] = true
   229  		case <-time.After(worstCase):
   230  			c.Fatalf("not enough changes: got %d, want %d", len(seen), N/2)
   231  		}
   232  	}
   233  	c.Assert(len(seen), gc.Equals, N/2)
   234  	assertNoChange(c, s.ch)
   235  }
   236  
   237  func (s *HubWatcherSuite) TestWatchCollection(c *gc.C) {
   238  	chA1 := make(chan watcher.Change)
   239  	chB1 := make(chan watcher.Change)
   240  	chA := make(chan watcher.Change)
   241  	chB := make(chan watcher.Change)
   242  
   243  	s.w.Watch("testA", 1, chA1)
   244  	s.w.Watch("testB", 1, chB1)
   245  	s.w.WatchCollection("testA", chA)
   246  	s.w.WatchCollection("testB", chB)
   247  
   248  	changes := []watcher.Change{
   249  		{"testA", 1, 3},
   250  		{"testA", 2, 2},
   251  		{"testB", 1, 5},
   252  		{"testB", 2, 6},
   253  	}
   254  	s.publish(c, changes...)
   255  
   256  	seen := map[chan<- watcher.Change][]watcher.Change{}
   257  
   258  	waitForChanges := func(count int, seen map[chan<- watcher.Change][]watcher.Change, timeout time.Duration) {
   259  		tooLong := time.After(timeout)
   260  		for n := 0; n < count; n++ {
   261  			select {
   262  			case chg := <-chA1:
   263  				seen[chA1] = append(seen[chA1], chg)
   264  			case chg := <-chB1:
   265  				seen[chB1] = append(seen[chB1], chg)
   266  			case chg := <-chA:
   267  				seen[chA] = append(seen[chA], chg)
   268  			case chg := <-chB:
   269  				seen[chB] = append(seen[chB], chg)
   270  			case <-tooLong:
   271  				return
   272  			}
   273  		}
   274  	}
   275  
   276  	waitForChanges(6, seen, testing.LongWait)
   277  
   278  	c.Check(seen[chA1], jc.DeepEquals, []watcher.Change{changes[0]})
   279  	c.Check(seen[chB1], jc.DeepEquals, []watcher.Change{changes[2]})
   280  	c.Check(seen[chA], jc.DeepEquals, []watcher.Change{changes[0], changes[1]})
   281  	c.Assert(seen[chB], jc.DeepEquals, []watcher.Change{changes[2], changes[3]})
   282  
   283  	s.w.UnwatchCollection("testB", chB)
   284  	s.w.Unwatch("testB", 1, chB1)
   285  
   286  	next := watcher.Change{"testA", 1, 4}
   287  	s.publish(c, next)
   288  
   289  	seen = map[chan<- watcher.Change][]watcher.Change{}
   290  	waitForChanges(2, seen, testing.LongWait)
   291  
   292  	c.Check(seen[chA1], gc.DeepEquals, []watcher.Change{next})
   293  	c.Check(seen[chB1], gc.IsNil)
   294  	c.Check(seen[chA], gc.DeepEquals, []watcher.Change{next})
   295  	c.Assert(seen[chB], gc.IsNil)
   296  
   297  	// Check that no extra events arrive.
   298  	seen = map[chan<- watcher.Change][]watcher.Change{}
   299  	waitForChanges(1, seen, testing.ShortWait)
   300  
   301  	c.Check(seen[chA1], gc.IsNil)
   302  	c.Check(seen[chB1], gc.IsNil)
   303  	c.Check(seen[chA], gc.IsNil)
   304  	c.Check(seen[chB], gc.IsNil)
   305  }
   306  
   307  func (s *HubWatcherSuite) TestUnwatchCollectionWithFilter(c *gc.C) {
   308  	filter := func(key interface{}) bool {
   309  		id := key.(int)
   310  		return id != 2
   311  	}
   312  
   313  	change := watcher.Change{"testA", 1, 3}
   314  	s.w.WatchCollectionWithFilter("testA", s.ch, filter)
   315  	s.publish(c, change)
   316  	assertChange(c, s.ch, change)
   317  	s.publish(c, watcher.Change{"testA", 2, 2})
   318  	assertNoChange(c, s.ch)
   319  
   320  	change = watcher.Change{"testA", 3, 3}
   321  	s.publish(c, change)
   322  	assertChange(c, s.ch, change)
   323  }
   324  
   325  func (s *HubWatcherSuite) TestWatchBeforeRemoveKnown(c *gc.C) {
   326  	added := watcher.Change{"test", "a", 2}
   327  	s.publish(c, added)
   328  
   329  	s.w.Watch("test", "a", s.ch)
   330  
   331  	removed := watcher.Change{"test", "a", -1}
   332  	s.publish(c, removed)
   333  	assertChange(c, s.ch, removed)
   334  }
   335  
   336  func (s *HubWatcherSuite) TestWatchStoppedWhileFlushing(c *gc.C) {
   337  	first := watcher.Change{"test", "a", 2}
   338  	second := watcher.Change{"test", "a", 3}
   339  
   340  	s.w.Watch("test", "a", s.ch)
   341  
   342  	s.publish(c, first)
   343  	// The second event forces a reallocation of the slice in the
   344  	// watcher.
   345  	s.publish(c, second)
   346  	// Unwatching should nil out the channel for all pending sync events.
   347  	s.w.Unwatch("test", "a", s.ch)
   348  
   349  	// Since we haven't removed anything off the channel before the
   350  	// unwatch, all the pending events should be cleared.
   351  	assertNoChange(c, s.ch)
   352  }