github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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  	"gopkg.in/tomb.v2"
    13  
    14  	"github.com/juju/juju/core/watcher"
    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  	Watcher NotifyWatcher
    70  }
    71  
    72  // NewNotifyWatcherC returns a NotifyWatcherC that checks for aggressive
    73  // event coalescence.
    74  func NewNotifyWatcherC(c *gc.C, w NotifyWatcher) NotifyWatcherC {
    75  	return NotifyWatcherC{
    76  		C:       c,
    77  		Watcher: w,
    78  	}
    79  }
    80  
    81  func (c NotifyWatcherC) AssertNoChange() {
    82  	select {
    83  	case _, ok := <-c.Watcher.Changes():
    84  		if ok {
    85  			c.Fatalf("watcher sent unexpected change")
    86  		} else {
    87  			c.Fatalf("watcher closed Changes channel")
    88  		}
    89  	case <-time.After(testing.ShortWait):
    90  	}
    91  }
    92  
    93  func (c NotifyWatcherC) AssertOneChange() {
    94  	longTimeout := time.After(testing.LongWait)
    95  loop:
    96  	for {
    97  		select {
    98  		case _, ok := <-c.Watcher.Changes():
    99  			c.C.Logf("got change")
   100  			c.Assert(ok, jc.IsTrue)
   101  			break loop
   102  		case <-longTimeout:
   103  			c.Fatalf("watcher did not send change")
   104  			break loop
   105  		}
   106  	}
   107  	c.AssertNoChange()
   108  }
   109  
   110  func (c NotifyWatcherC) AssertAtleastOneChange() {
   111  	longTimeout := time.After(testing.LongWait)
   112  loop:
   113  	for {
   114  		select {
   115  		case _, ok := <-c.Watcher.Changes():
   116  			c.C.Logf("got change")
   117  			c.Assert(ok, jc.IsTrue)
   118  			break loop
   119  		case <-longTimeout:
   120  			c.Fatalf("watcher did not send change")
   121  			break loop
   122  		}
   123  	}
   124  }
   125  
   126  // TODO(quiescence): reimplement quiescence and delete this utility
   127  func (c NotifyWatcherC) AssertChanges(n int) {
   128  	longTimeout := time.After(testing.LongWait)
   129  	got := 0
   130  loop:
   131  	for got < n {
   132  		select {
   133  		case _, ok := <-c.Watcher.Changes():
   134  			c.C.Logf("got change")
   135  			c.Assert(ok, jc.IsTrue)
   136  			got++
   137  		case <-longTimeout:
   138  			c.Fatalf("watcher did not %d send change(s)", n-got)
   139  			break loop
   140  		}
   141  	}
   142  	c.AssertNoChange()
   143  }
   144  
   145  func (c NotifyWatcherC) AssertClosed() {
   146  	select {
   147  	case _, ok := <-c.Watcher.Changes():
   148  		c.Assert(ok, jc.IsFalse)
   149  	default:
   150  		c.Fatalf("watcher not closed")
   151  	}
   152  }
   153  
   154  // StringsWatcherC embeds a gocheck.C and adds methods to help verify
   155  // the behaviour of any watcher that uses a <-chan []string.
   156  type StringsWatcherC struct {
   157  	*gc.C
   158  	Watcher StringsWatcher
   159  }
   160  
   161  // NewStringsWatcherC returns a StringsWatcherC that checks for aggressive
   162  // event coalescence.
   163  func NewStringsWatcherC(c *gc.C, w StringsWatcher) StringsWatcherC {
   164  	return StringsWatcherC{
   165  		C:       c,
   166  		Watcher: w,
   167  	}
   168  }
   169  
   170  type StringsWatcher interface {
   171  	Stop() error
   172  	Changes() <-chan []string
   173  }
   174  
   175  func (c StringsWatcherC) AssertNoChange() {
   176  	select {
   177  	case actual, ok := <-c.Watcher.Changes():
   178  		c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok)
   179  	case <-time.After(testing.ShortWait):
   180  	}
   181  }
   182  
   183  func (c StringsWatcherC) AssertChanges() {
   184  	select {
   185  	case <-c.Watcher.Changes():
   186  	case <-time.After(testing.LongWait):
   187  		c.Fatalf("watcher did not send change")
   188  	}
   189  }
   190  
   191  func (c StringsWatcherC) AssertChange(expect ...string) {
   192  	// We should assert for either a single or multiple changes,
   193  	// based on the number of `expect` changes.
   194  	c.assertChange(len(expect) == 1, expect...)
   195  }
   196  
   197  func (c StringsWatcherC) AssertChangeInSingleEvent(expect ...string) {
   198  	c.assertChange(true, expect...)
   199  }
   200  
   201  // AssertChangeMaybeIncluding verifies that there is a change that may
   202  // contain zero to all of the passed in strings, and no other changes.
   203  func (c StringsWatcherC) AssertChangeMaybeIncluding(expect ...string) {
   204  	maxCount := len(expect)
   205  	actual := c.collectChanges(true, maxCount)
   206  
   207  	if maxCount == 0 {
   208  		c.Assert(actual, gc.HasLen, 0)
   209  	} else {
   210  		actualCount := len(actual)
   211  		c.Assert(actualCount <= maxCount, jc.IsTrue, gc.Commentf("expected at most %d, got %d", maxCount, actualCount))
   212  		unexpected := set.NewStrings(actual...).Difference(set.NewStrings(expect...))
   213  		c.Assert(unexpected.Values(), gc.HasLen, 0)
   214  	}
   215  }
   216  
   217  // assertChange asserts the given list of changes was reported by
   218  // the watcher, but does not assume there are no following changes.
   219  func (c StringsWatcherC) assertChange(single bool, expect ...string) {
   220  	actual := c.collectChanges(single, len(expect))
   221  	if len(expect) == 0 {
   222  		c.Assert(actual, gc.HasLen, 0)
   223  	} else {
   224  		c.Assert(actual, jc.SameContents, expect)
   225  	}
   226  }
   227  
   228  // collectChanges gets up to the max number of changes within the
   229  // testing.LongWait period.
   230  func (c StringsWatcherC) collectChanges(single bool, max int) []string {
   231  	timeout := time.After(testing.LongWait)
   232  	var actual []string
   233  	gotOneChange := false
   234  loop:
   235  	for {
   236  		select {
   237  		case changes, ok := <-c.Watcher.Changes():
   238  			c.Assert(ok, jc.IsTrue)
   239  			gotOneChange = true
   240  			actual = append(actual, changes...)
   241  			if single || len(actual) >= max {
   242  				break loop
   243  			}
   244  		case <-timeout:
   245  			if !gotOneChange {
   246  				c.Fatalf("watcher did not send change")
   247  			}
   248  			// If we triggered a timeout, stop looking for more changes
   249  			break loop
   250  		}
   251  	}
   252  	return actual
   253  }
   254  
   255  func (c StringsWatcherC) AssertClosed() {
   256  	select {
   257  	case _, ok := <-c.Watcher.Changes():
   258  		c.Assert(ok, jc.IsFalse)
   259  	default:
   260  		c.Fatalf("watcher not closed")
   261  	}
   262  }
   263  
   264  // RelationUnitsWatcherC embeds a gocheck.C and adds methods to help
   265  // verify the behaviour of any watcher that uses a <-chan
   266  // params.RelationUnitsChange.
   267  type RelationUnitsWatcherC struct {
   268  	*gc.C
   269  	Watcher RelationUnitsWatcher
   270  	// settingsVersions keeps track of the settings version of each
   271  	// changed unit since the last received changes to ensure version
   272  	// always increases.
   273  	settingsVersions    map[string]int64
   274  	appSettingsVersions map[string]int64
   275  }
   276  
   277  // NewRelationUnitsWatcherC returns a RelationUnitsWatcherC that
   278  // checks for aggressive event coalescence.
   279  func NewRelationUnitsWatcherC(c *gc.C, w RelationUnitsWatcher) RelationUnitsWatcherC {
   280  	return RelationUnitsWatcherC{
   281  		C:                   c,
   282  		Watcher:             w,
   283  		settingsVersions:    make(map[string]int64),
   284  		appSettingsVersions: make(map[string]int64),
   285  	}
   286  }
   287  
   288  type RelationUnitsWatcher interface {
   289  	Stop() error
   290  	Changes() watcher.RelationUnitsChannel
   291  }
   292  
   293  func (c RelationUnitsWatcherC) AssertNoChange() {
   294  	select {
   295  	case actual, ok := <-c.Watcher.Changes():
   296  		c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok)
   297  	case <-time.After(testing.ShortWait):
   298  	}
   299  }
   300  
   301  // AssertChange asserts the given changes was reported by the watcher,
   302  // but does not assume there are no following changes.
   303  func (c RelationUnitsWatcherC) AssertChange(changed []string, appChanged []string, departed []string) {
   304  	// Get all items in changed in a map for easy lookup.
   305  	changedNames := set.NewStrings(changed...)
   306  	appChangedNames := set.NewStrings(appChanged...)
   307  	timeout := time.After(testing.LongWait)
   308  	select {
   309  	case actual, ok := <-c.Watcher.Changes():
   310  		c.Logf("Watcher.Changes() => %# v", actual)
   311  		c.Assert(ok, jc.IsTrue)
   312  		c.Check(actual.Changed, gc.HasLen, len(changed))
   313  		c.Check(actual.AppChanged, gc.HasLen, len(appChanged))
   314  		// Because the versions can change, we only need to make sure
   315  		// the keys match, not the contents (UnitSettings == txnRevno).
   316  		for k, settings := range actual.Changed {
   317  			c.Check(changedNames.Contains(k), jc.IsTrue)
   318  			oldVer, ok := c.settingsVersions[k]
   319  			if !ok {
   320  				// TODO(jam): 2019-10-22 shouldn't we update this *every* time we see it?
   321  				// This is the first time we see this unit, so
   322  				// save the settings version for later.
   323  				c.settingsVersions[k] = settings.Version
   324  			} else {
   325  				// Already seen; make sure the version increased.
   326  				c.Assert(settings.Version, jc.GreaterThan, oldVer,
   327  					gc.Commentf("expected unit settings to increase got %d had %d",
   328  						settings.Version, oldVer))
   329  			}
   330  		}
   331  		for k, version := range actual.AppChanged {
   332  			c.Check(appChangedNames.Contains(k), jc.IsTrue)
   333  			oldVer, ok := c.appSettingsVersions[k]
   334  			if ok {
   335  				// Make sure if we've seen this setting before, it has been updated
   336  				c.Assert(version, jc.GreaterThan, oldVer,
   337  					gc.Commentf("expected app settings to increase got %d had %d",
   338  						version, oldVer))
   339  			}
   340  			c.appSettingsVersions[k] = version
   341  		}
   342  		c.Check(actual.Departed, jc.SameContents, departed)
   343  	case <-timeout:
   344  		c.Fatalf("watcher did not send change")
   345  	}
   346  }
   347  
   348  func (c RelationUnitsWatcherC) AssertClosed() {
   349  	select {
   350  	case _, ok := <-c.Watcher.Changes():
   351  		c.Assert(ok, jc.IsFalse)
   352  	default:
   353  		c.Fatalf("watcher not closed")
   354  	}
   355  }
   356  
   357  // SecretsTriggerWatcherC embeds a gocheck.C and adds methods to help
   358  // verify the behaviour of any watcher that uses a
   359  // <-chan []SecretTriggerChange
   360  type SecretsTriggerWatcherC struct {
   361  	*gc.C
   362  	Watcher SecretsTriggerWatcher
   363  }
   364  
   365  // NewSecretsTriggerWatcherC returns a SecretsTriggerWatcherC that
   366  // checks for aggressive event coalescence.
   367  func NewSecretsTriggerWatcherC(c *gc.C, w SecretsTriggerWatcher) SecretsTriggerWatcherC {
   368  	return SecretsTriggerWatcherC{
   369  		C:       c,
   370  		Watcher: w,
   371  	}
   372  }
   373  
   374  type SecretsTriggerWatcher interface {
   375  	Stop() error
   376  	Changes() watcher.SecretTriggerChannel
   377  }
   378  
   379  func (c SecretsTriggerWatcherC) AssertNoChange() {
   380  	select {
   381  	case actual, ok := <-c.Watcher.Changes():
   382  		c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok)
   383  	case <-time.After(testing.ShortWait):
   384  	}
   385  }
   386  
   387  // AssertChange asserts the given changes was reported by the watcher,
   388  // but does not assume there are no following changes.
   389  func (c SecretsTriggerWatcherC) AssertChange(expect ...watcher.SecretTriggerChange) {
   390  	var received []watcher.SecretTriggerChange
   391  	timeout := time.After(testing.LongWait)
   392  	for a := testing.LongAttempt.Start(); a.Next(); {
   393  		select {
   394  		case actual, ok := <-c.Watcher.Changes():
   395  			c.Logf("Secrets Trigger Watcher.Changes() => %# v", actual)
   396  			c.Assert(ok, jc.IsTrue)
   397  			received = append(received, actual...)
   398  			if len(received) >= len(expect) {
   399  				mc := jc.NewMultiChecker()
   400  				mc.AddExpr(`_[_].NextTriggerTime`, jc.Almost, jc.ExpectedValue)
   401  				c.Assert(received, mc, expect)
   402  				return
   403  			}
   404  		case <-timeout:
   405  			c.Fatalf("watcher did not send change")
   406  		}
   407  	}
   408  }
   409  
   410  func (c SecretsTriggerWatcherC) AssertClosed() {
   411  	select {
   412  	case _, ok := <-c.Watcher.Changes():
   413  		c.Assert(ok, jc.IsFalse)
   414  	default:
   415  		c.Fatalf("watcher not closed")
   416  	}
   417  }
   418  
   419  // SecretBackendRotateWatcherC embeds a gocheck.C and adds methods to help
   420  // verify the behaviour of any watcher that uses a
   421  // <-chan []SecretBackendRotateChange
   422  type SecretBackendRotateWatcherC struct {
   423  	*gc.C
   424  	Watcher SecretBackendRotateWatcher
   425  }
   426  
   427  // NewSecretBackendRotateWatcherC returns a SecretBackendRotateWatcherC that
   428  // checks for aggressive event coalescence.
   429  func NewSecretBackendRotateWatcherC(c *gc.C, w SecretBackendRotateWatcher) SecretBackendRotateWatcherC {
   430  	return SecretBackendRotateWatcherC{
   431  		C:       c,
   432  		Watcher: w,
   433  	}
   434  }
   435  
   436  type SecretBackendRotateWatcher interface {
   437  	Stop() error
   438  	Changes() watcher.SecretBackendRotateChannel
   439  }
   440  
   441  func (c SecretBackendRotateWatcherC) AssertNoChange() {
   442  	select {
   443  	case actual, ok := <-c.Watcher.Changes():
   444  		c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok)
   445  	case <-time.After(testing.ShortWait):
   446  	}
   447  }
   448  
   449  // AssertChange asserts the given changes was reported by the watcher,
   450  // but does not assume there are no following changes.
   451  func (c SecretBackendRotateWatcherC) AssertChange(expect ...watcher.SecretBackendRotateChange) {
   452  	var received []watcher.SecretBackendRotateChange
   453  	timeout := time.After(testing.LongWait)
   454  	for a := testing.LongAttempt.Start(); a.Next(); {
   455  		select {
   456  		case actual, ok := <-c.Watcher.Changes():
   457  			c.Logf("Secrets Trigger Watcher.Changes() => %# v", actual)
   458  			c.Assert(ok, jc.IsTrue)
   459  			received = append(received, actual...)
   460  			if len(received) >= len(expect) {
   461  				mc := jc.NewMultiChecker()
   462  				mc.AddExpr(`_[_].NextTriggerTime`, jc.Almost, jc.ExpectedValue)
   463  				c.Assert(received, mc, expect)
   464  				return
   465  			}
   466  		case <-timeout:
   467  			c.Fatalf("watcher did not send change")
   468  		}
   469  	}
   470  }
   471  
   472  func (c SecretBackendRotateWatcherC) AssertClosed() {
   473  	select {
   474  	case _, ok := <-c.Watcher.Changes():
   475  		c.Assert(ok, jc.IsFalse)
   476  	default:
   477  		c.Fatalf("watcher not closed")
   478  	}
   479  }
   480  
   481  // MockNotifyWatcher implements state.NotifyWatcher.
   482  type MockNotifyWatcher struct {
   483  	tomb tomb.Tomb
   484  	ch   <-chan struct{}
   485  }
   486  
   487  func NewMockNotifyWatcher(ch <-chan struct{}) *MockNotifyWatcher {
   488  	w := &MockNotifyWatcher{ch: ch}
   489  	w.tomb.Go(func() error {
   490  		<-w.tomb.Dying()
   491  		return tomb.ErrDying
   492  	})
   493  	return w
   494  }
   495  
   496  func (w *MockNotifyWatcher) Changes() <-chan struct{} {
   497  	return w.ch
   498  }
   499  
   500  func (w *MockNotifyWatcher) Stop() error {
   501  	w.Kill()
   502  	return w.Wait()
   503  }
   504  
   505  func (w *MockNotifyWatcher) Kill() {
   506  	w.tomb.Kill(nil)
   507  }
   508  
   509  func (w *MockNotifyWatcher) Err() error {
   510  	return w.tomb.Err()
   511  }
   512  
   513  func (w *MockNotifyWatcher) Wait() error {
   514  	return w.tomb.Wait()
   515  }
   516  
   517  // MockStringsWatcher implements state.StringsWatcher.
   518  type MockStringsWatcher struct {
   519  	tomb tomb.Tomb
   520  	ch   <-chan []string
   521  }
   522  
   523  func NewMockStringsWatcher(ch <-chan []string) *MockStringsWatcher {
   524  	w := &MockStringsWatcher{ch: ch}
   525  	w.tomb.Go(func() error {
   526  		<-w.tomb.Dying()
   527  		return tomb.ErrDying
   528  	})
   529  	return w
   530  }
   531  
   532  func (w *MockStringsWatcher) Changes() <-chan []string {
   533  	return w.ch
   534  }
   535  
   536  func (w *MockStringsWatcher) Stop() error {
   537  	w.Kill()
   538  	return w.Wait()
   539  }
   540  
   541  func (w *MockStringsWatcher) Kill() {
   542  	w.tomb.Kill(nil)
   543  }
   544  
   545  func (w *MockStringsWatcher) Err() error {
   546  	return w.tomb.Err()
   547  }
   548  
   549  func (w *MockStringsWatcher) Wait() error {
   550  	return w.tomb.Wait()
   551  }