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

     1  // Copyright 2012-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package testing
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/collections/set"
    10  	jc "github.com/juju/testing/checkers"
    11  	gc "gopkg.in/check.v1"
    12  	tomb "gopkg.in/tomb.v2"
    13  
    14  	"github.com/juju/juju/apiserver/params"
    15  	"github.com/juju/juju/testing"
    16  )
    17  
    18  type Stopper interface {
    19  	Stop() error
    20  }
    21  
    22  func AssertStop(c *gc.C, stopper Stopper) {
    23  	c.Assert(stopper.Stop(), gc.IsNil)
    24  }
    25  
    26  type KillWaiter interface {
    27  	Kill()
    28  	Wait() error
    29  }
    30  
    31  func AssertKillAndWait(c *gc.C, killWaiter KillWaiter) {
    32  	killWaiter.Kill()
    33  	c.Assert(killWaiter.Wait(), jc.ErrorIsNil)
    34  }
    35  
    36  // AssertCanStopWhenSending ensures even when there are changes
    37  // pending to be delivered by the watcher it can still stop
    38  // cleanly. This is necessary to check for deadlocks in case the
    39  // watcher's inner loop is blocked trying to send and its tomb is
    40  // already dying.
    41  func AssertCanStopWhenSending(c *gc.C, stopper Stopper) {
    42  	// Leave some time for the event to be delivered and the watcher
    43  	// to block on sending it.
    44  	<-time.After(testing.ShortWait)
    45  	stopped := make(chan bool)
    46  	// Stop() blocks, so we need to call it in a separate goroutine.
    47  	go func() {
    48  		c.Check(stopper.Stop(), gc.IsNil)
    49  		stopped <- true
    50  	}()
    51  	select {
    52  	case <-time.After(testing.LongWait):
    53  		// NOTE: If this test fails here it means we have a deadlock
    54  		// in the client-side watcher implementation.
    55  		c.Fatalf("watcher did not stop as expected")
    56  	case <-stopped:
    57  	}
    58  }
    59  
    60  type NotifyWatcher interface {
    61  	Stop() error
    62  	Changes() <-chan struct{}
    63  }
    64  
    65  // NotifyWatcherC embeds a gocheck.C and adds methods to help verify
    66  // the behaviour of any watcher that uses a <-chan struct{}.
    67  type NotifyWatcherC struct {
    68  	*gc.C
    69  	State   SyncStarter
    70  	Watcher NotifyWatcher
    71  }
    72  
    73  // SyncStarter is an interface that watcher checkers will use to ensure
    74  // that changes to the watched object have been synchronized. This is
    75  // primarily implemented by state.State.
    76  type SyncStarter interface {
    77  	StartSync()
    78  }
    79  
    80  // NewNotifyWatcherC returns a NotifyWatcherC that checks for aggressive
    81  // event coalescence.
    82  func NewNotifyWatcherC(c *gc.C, st SyncStarter, w NotifyWatcher) NotifyWatcherC {
    83  	return NotifyWatcherC{
    84  		C:       c,
    85  		State:   st,
    86  		Watcher: w,
    87  	}
    88  }
    89  
    90  func (c NotifyWatcherC) AssertNoChange() {
    91  	c.State.StartSync()
    92  	select {
    93  	case _, ok := <-c.Watcher.Changes():
    94  		c.Fatalf("watcher sent unexpected change: (_, %v)", ok)
    95  	case <-time.After(testing.ShortWait):
    96  	}
    97  }
    98  
    99  func (c NotifyWatcherC) AssertOneChange() {
   100  	// Wait a very small amount of time, so that if there is already an event
   101  	// queued to be processed, we see it, before the StartSync flushes new
   102  	// events into the queue.
   103  	shortTimeout := time.After(1 * time.Millisecond)
   104  	longTimeout := time.After(testing.LongWait)
   105  loop:
   106  	for {
   107  		select {
   108  		case _, ok := <-c.Watcher.Changes():
   109  			c.C.Logf("got change")
   110  			c.Assert(ok, jc.IsTrue)
   111  			break loop
   112  		case <-shortTimeout:
   113  			c.C.Logf("StartSync()")
   114  			c.State.StartSync()
   115  			shortTimeout = nil
   116  		case <-longTimeout:
   117  			c.Fatalf("watcher did not send change")
   118  			break loop
   119  		}
   120  	}
   121  	c.AssertNoChange()
   122  }
   123  
   124  func (c NotifyWatcherC) AssertClosed() {
   125  	select {
   126  	case _, ok := <-c.Watcher.Changes():
   127  		c.Assert(ok, jc.IsFalse)
   128  	default:
   129  		c.Fatalf("watcher not closed")
   130  	}
   131  }
   132  
   133  // StringsWatcherC embeds a gocheck.C and adds methods to help verify
   134  // the behaviour of any watcher that uses a <-chan []string.
   135  type StringsWatcherC struct {
   136  	*gc.C
   137  	State   SyncStarter
   138  	Watcher StringsWatcher
   139  }
   140  
   141  // NewStringsWatcherC returns a StringsWatcherC that checks for aggressive
   142  // event coalescence.
   143  func NewStringsWatcherC(c *gc.C, st SyncStarter, w StringsWatcher) StringsWatcherC {
   144  	return StringsWatcherC{
   145  		C:       c,
   146  		State:   st,
   147  		Watcher: w,
   148  	}
   149  }
   150  
   151  type StringsWatcher interface {
   152  	Stop() error
   153  	Changes() <-chan []string
   154  }
   155  
   156  func (c StringsWatcherC) AssertNoChange() {
   157  	c.State.StartSync()
   158  	select {
   159  	case actual, ok := <-c.Watcher.Changes():
   160  		c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok)
   161  	case <-time.After(testing.ShortWait):
   162  	}
   163  }
   164  
   165  func (c StringsWatcherC) AssertChanges() {
   166  	c.State.StartSync()
   167  	select {
   168  	case <-c.Watcher.Changes():
   169  	case <-time.After(testing.LongWait):
   170  		c.Fatalf("watcher did not send change")
   171  	}
   172  }
   173  
   174  func (c StringsWatcherC) AssertChange(expect ...string) {
   175  	// We should assert for either a single or multiple changes,
   176  	// based on the number of `expect` changes.
   177  	c.assertChange(len(expect) == 1, expect...)
   178  }
   179  
   180  func (c StringsWatcherC) AssertChangeInSingleEvent(expect ...string) {
   181  	c.assertChange(true, expect...)
   182  }
   183  
   184  // AssertChangeMaybeIncluding verifies that there is a change that may
   185  // contain zero to all of the passed in strings, and no other changes.
   186  func (c StringsWatcherC) AssertChangeMaybeIncluding(expect ...string) {
   187  	maxCount := len(expect)
   188  	actual := c.collectChanges(true, maxCount)
   189  
   190  	if maxCount == 0 {
   191  		c.Assert(actual, gc.HasLen, 0)
   192  	} else {
   193  		actualCount := len(actual)
   194  		c.Assert(actualCount <= maxCount, jc.IsTrue, gc.Commentf("expected at most %d, got %d", maxCount, actualCount))
   195  		unexpected := set.NewStrings(actual...).Difference(set.NewStrings(expect...))
   196  		c.Assert(unexpected.Values(), gc.HasLen, 0)
   197  	}
   198  }
   199  
   200  // assertChange asserts the given list of changes was reported by
   201  // the watcher, but does not assume there are no following changes.
   202  func (c StringsWatcherC) assertChange(single bool, expect ...string) {
   203  	actual := c.collectChanges(single, len(expect))
   204  	if len(expect) == 0 {
   205  		c.Assert(actual, gc.HasLen, 0)
   206  	} else {
   207  		c.Assert(actual, jc.SameContents, expect)
   208  	}
   209  }
   210  
   211  // collectChanges gets up to the max number of changes within the
   212  // testing.LongWait period.
   213  func (c StringsWatcherC) collectChanges(single bool, max int) []string {
   214  	c.State.StartSync()
   215  	timeout := time.After(testing.LongWait)
   216  	var actual []string
   217  	gotOneChange := false
   218  loop:
   219  	for {
   220  		select {
   221  		case changes, ok := <-c.Watcher.Changes():
   222  			c.Assert(ok, jc.IsTrue)
   223  			gotOneChange = true
   224  			actual = append(actual, changes...)
   225  			if single || len(actual) >= max {
   226  				break loop
   227  			}
   228  		case <-timeout:
   229  			if !gotOneChange {
   230  				c.Fatalf("watcher did not send change")
   231  			}
   232  			// If we triggered a timeout, stop looking for more changes
   233  			break loop
   234  		}
   235  	}
   236  	return actual
   237  }
   238  
   239  func (c StringsWatcherC) AssertClosed() {
   240  	select {
   241  	case _, ok := <-c.Watcher.Changes():
   242  		c.Assert(ok, jc.IsFalse)
   243  	default:
   244  		c.Fatalf("watcher not closed")
   245  	}
   246  }
   247  
   248  // RelationUnitsWatcherC embeds a gocheck.C and adds methods to help
   249  // verify the behaviour of any watcher that uses a <-chan
   250  // params.RelationUnitsChange.
   251  type RelationUnitsWatcherC struct {
   252  	*gc.C
   253  	State   SyncStarter
   254  	Watcher RelationUnitsWatcher
   255  	// settingsVersions keeps track of the settings version of each
   256  	// changed unit since the last received changes to ensure version
   257  	// always increases.
   258  	settingsVersions map[string]int64
   259  }
   260  
   261  // NewRelationUnitsWatcherC returns a RelationUnitsWatcherC that
   262  // checks for aggressive event coalescence.
   263  func NewRelationUnitsWatcherC(c *gc.C, st SyncStarter, w RelationUnitsWatcher) RelationUnitsWatcherC {
   264  	return RelationUnitsWatcherC{
   265  		C:                c,
   266  		State:            st,
   267  		Watcher:          w,
   268  		settingsVersions: make(map[string]int64),
   269  	}
   270  }
   271  
   272  type RelationUnitsWatcher interface {
   273  	Stop() error
   274  	Changes() <-chan params.RelationUnitsChange
   275  }
   276  
   277  func (c RelationUnitsWatcherC) AssertNoChange() {
   278  	c.State.StartSync()
   279  	select {
   280  	case actual, ok := <-c.Watcher.Changes():
   281  		c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok)
   282  	case <-time.After(testing.ShortWait):
   283  	}
   284  }
   285  
   286  // AssertChange asserts the given changes was reported by the watcher,
   287  // but does not assume there are no following changes.
   288  func (c RelationUnitsWatcherC) AssertChange(changed []string, departed []string) {
   289  	// Get all items in changed in a map for easy lookup.
   290  	changedNames := make(map[string]bool)
   291  	for _, name := range changed {
   292  		changedNames[name] = true
   293  	}
   294  	c.State.StartSync()
   295  	timeout := time.After(testing.LongWait)
   296  	select {
   297  	case actual, ok := <-c.Watcher.Changes():
   298  		c.Assert(ok, jc.IsTrue)
   299  		c.Assert(actual.Changed, gc.HasLen, len(changed))
   300  		// Because the versions can change, we only need to make sure
   301  		// the keys match, not the contents (UnitSettings == txnRevno).
   302  		for k, settings := range actual.Changed {
   303  			_, ok := changedNames[k]
   304  			c.Assert(ok, jc.IsTrue)
   305  			oldVer, ok := c.settingsVersions[k]
   306  			if !ok {
   307  				// This is the first time we see this unit, so
   308  				// save the settings version for later.
   309  				c.settingsVersions[k] = settings.Version
   310  			} else {
   311  				// Already seen; make sure the version increased.
   312  				if settings.Version <= oldVer {
   313  					c.Fatalf("expected unit settings version > %d (got %d)", oldVer, settings.Version)
   314  				}
   315  			}
   316  		}
   317  		c.Assert(actual.Departed, jc.SameContents, departed)
   318  	case <-timeout:
   319  		c.Fatalf("watcher did not send change")
   320  	}
   321  }
   322  
   323  func (c RelationUnitsWatcherC) AssertClosed() {
   324  	select {
   325  	case _, ok := <-c.Watcher.Changes():
   326  		c.Assert(ok, jc.IsFalse)
   327  	default:
   328  		c.Fatalf("watcher not closed")
   329  	}
   330  }
   331  
   332  // MockNotifyWatcher implements state.NotifyWatcher.
   333  type MockNotifyWatcher struct {
   334  	tomb tomb.Tomb
   335  	ch   <-chan struct{}
   336  }
   337  
   338  func NewMockNotifyWatcher(ch <-chan struct{}) *MockNotifyWatcher {
   339  	w := &MockNotifyWatcher{ch: ch}
   340  	w.tomb.Go(func() error {
   341  		<-w.tomb.Dying()
   342  		return tomb.ErrDying
   343  	})
   344  	return w
   345  }
   346  
   347  func (w *MockNotifyWatcher) Changes() <-chan struct{} {
   348  	return w.ch
   349  }
   350  
   351  func (w *MockNotifyWatcher) Stop() error {
   352  	w.Kill()
   353  	return w.Wait()
   354  }
   355  
   356  func (w *MockNotifyWatcher) Kill() {
   357  	w.tomb.Kill(nil)
   358  }
   359  
   360  func (w *MockNotifyWatcher) Err() error {
   361  	return w.tomb.Err()
   362  }
   363  
   364  func (w *MockNotifyWatcher) Wait() error {
   365  	return w.tomb.Wait()
   366  }
   367  
   368  // MockStringsWatcher implements state.StringsWatcher.
   369  type MockStringsWatcher struct {
   370  	tomb tomb.Tomb
   371  	ch   <-chan []string
   372  }
   373  
   374  func NewMockStringsWatcher(ch <-chan []string) *MockStringsWatcher {
   375  	w := &MockStringsWatcher{ch: ch}
   376  	w.tomb.Go(func() error {
   377  		<-w.tomb.Dying()
   378  		return tomb.ErrDying
   379  	})
   380  	return w
   381  }
   382  
   383  func (w *MockStringsWatcher) Changes() <-chan []string {
   384  	return w.ch
   385  }
   386  
   387  func (w *MockStringsWatcher) Stop() error {
   388  	w.Kill()
   389  	return w.Wait()
   390  }
   391  
   392  func (w *MockStringsWatcher) Kill() {
   393  	w.tomb.Kill(nil)
   394  }
   395  
   396  func (w *MockStringsWatcher) Err() error {
   397  	return w.tomb.Err()
   398  }
   399  
   400  func (w *MockStringsWatcher) Wait() error {
   401  	return w.tomb.Wait()
   402  }