github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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  	jc "github.com/juju/testing/checkers"
    10  	"github.com/juju/utils/set"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	"github.com/juju/juju/apiserver/params"
    14  	"github.com/juju/juju/testing"
    15  )
    16  
    17  type Stopper interface {
    18  	Stop() error
    19  }
    20  
    21  func AssertStop(c *gc.C, stopper Stopper) {
    22  	c.Assert(stopper.Stop(), gc.IsNil)
    23  }
    24  
    25  type KillWaiter interface {
    26  	Kill()
    27  	Wait() error
    28  }
    29  
    30  func AssertKillAndWait(c *gc.C, killWaiter KillWaiter) {
    31  	killWaiter.Kill()
    32  	c.Assert(killWaiter.Wait(), gc.IsNil)
    33  }
    34  
    35  // AssertCanStopWhenSending ensures even when there are changes
    36  // pending to be delivered by the watcher it can still stop
    37  // cleanly. This is necessary to check for deadlocks in case the
    38  // watcher's inner loop is blocked trying to send and its tomb is
    39  // already dying.
    40  func AssertCanStopWhenSending(c *gc.C, stopper Stopper) {
    41  	// Leave some time for the event to be delivered and the watcher
    42  	// to block on sending it.
    43  	<-time.After(testing.ShortWait)
    44  	stopped := make(chan bool)
    45  	// Stop() blocks, so we need to call it in a separate goroutine.
    46  	go func() {
    47  		c.Check(stopper.Stop(), gc.IsNil)
    48  		stopped <- true
    49  	}()
    50  	select {
    51  	case <-time.After(testing.LongWait):
    52  		// NOTE: If this test fails here it means we have a deadlock
    53  		// in the client-side watcher implementation.
    54  		c.Fatalf("watcher did not stop as expected")
    55  	case <-stopped:
    56  	}
    57  }
    58  
    59  type NotifyWatcher interface {
    60  	Stop() error
    61  	Changes() <-chan struct{}
    62  }
    63  
    64  // NotifyWatcherC embeds a gocheck.C and adds methods to help verify
    65  // the behaviour of any watcher that uses a <-chan struct{}.
    66  type NotifyWatcherC struct {
    67  	*gc.C
    68  	State   SyncStarter
    69  	Watcher NotifyWatcher
    70  }
    71  
    72  // SyncStarter is an interface that watcher checkers will use to ensure
    73  // that changes to the watched object have been synchronized. This is
    74  // primarily implemented by state.State.
    75  type SyncStarter interface {
    76  	StartSync()
    77  }
    78  
    79  // NewNotifyWatcherC returns a NotifyWatcherC that checks for aggressive
    80  // event coalescence.
    81  func NewNotifyWatcherC(c *gc.C, st SyncStarter, w NotifyWatcher) NotifyWatcherC {
    82  	return NotifyWatcherC{
    83  		C:       c,
    84  		State:   st,
    85  		Watcher: w,
    86  	}
    87  }
    88  
    89  func (c NotifyWatcherC) AssertNoChange() {
    90  	c.State.StartSync()
    91  	select {
    92  	case _, ok := <-c.Watcher.Changes():
    93  		c.Fatalf("watcher sent unexpected change: (_, %v)", ok)
    94  	case <-time.After(testing.ShortWait):
    95  	}
    96  }
    97  
    98  func (c NotifyWatcherC) AssertOneChange() {
    99  	c.State.StartSync()
   100  	select {
   101  	case _, ok := <-c.Watcher.Changes():
   102  		c.Assert(ok, jc.IsTrue)
   103  	case <-time.After(testing.LongWait):
   104  		c.Fatalf("watcher did not send change")
   105  	}
   106  	c.AssertNoChange()
   107  }
   108  
   109  func (c NotifyWatcherC) AssertClosed() {
   110  	select {
   111  	case _, ok := <-c.Watcher.Changes():
   112  		c.Assert(ok, jc.IsFalse)
   113  	default:
   114  		c.Fatalf("watcher not closed")
   115  	}
   116  }
   117  
   118  // StringsWatcherC embeds a gocheck.C and adds methods to help verify
   119  // the behaviour of any watcher that uses a <-chan []string.
   120  type StringsWatcherC struct {
   121  	*gc.C
   122  	State   SyncStarter
   123  	Watcher StringsWatcher
   124  }
   125  
   126  // NewStringsWatcherC returns a StringsWatcherC that checks for aggressive
   127  // event coalescence.
   128  func NewStringsWatcherC(c *gc.C, st SyncStarter, w StringsWatcher) StringsWatcherC {
   129  	return StringsWatcherC{
   130  		C:       c,
   131  		State:   st,
   132  		Watcher: w,
   133  	}
   134  }
   135  
   136  type StringsWatcher interface {
   137  	Stop() error
   138  	Changes() <-chan []string
   139  }
   140  
   141  func (c StringsWatcherC) AssertNoChange() {
   142  	c.State.StartSync()
   143  	select {
   144  	case actual, ok := <-c.Watcher.Changes():
   145  		c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok)
   146  	case <-time.After(testing.ShortWait):
   147  	}
   148  }
   149  
   150  func (c StringsWatcherC) AssertChanges() {
   151  	c.State.StartSync()
   152  	select {
   153  	case <-c.Watcher.Changes():
   154  	case <-time.After(testing.LongWait):
   155  		c.Fatalf("watcher did not send change")
   156  	}
   157  }
   158  
   159  func (c StringsWatcherC) AssertChange(expect ...string) {
   160  	c.assertChange(false, expect...)
   161  }
   162  
   163  func (c StringsWatcherC) AssertChangeInSingleEvent(expect ...string) {
   164  	c.assertChange(true, expect...)
   165  }
   166  
   167  // AssertChangeMaybeIncluding verifies that there is a change that may
   168  // contain zero to all of the passed in strings, and no other changes.
   169  func (c StringsWatcherC) AssertChangeMaybeIncluding(expect ...string) {
   170  	maxCount := len(expect)
   171  	actual := c.collectChanges(true, maxCount)
   172  
   173  	if maxCount == 0 {
   174  		c.Assert(actual, gc.HasLen, 0)
   175  	} else {
   176  		actualCount := len(actual)
   177  		c.Assert(actualCount <= maxCount, jc.IsTrue, gc.Commentf("expected at most %d, got %d", maxCount, actualCount))
   178  		unexpected := set.NewStrings(actual...).Difference(set.NewStrings(expect...))
   179  		c.Assert(unexpected.Values(), gc.HasLen, 0)
   180  	}
   181  }
   182  
   183  // assertChange asserts the given list of changes was reported by
   184  // the watcher, but does not assume there are no following changes.
   185  func (c StringsWatcherC) assertChange(single bool, expect ...string) {
   186  	actual := c.collectChanges(single, len(expect))
   187  	if len(expect) == 0 {
   188  		c.Assert(actual, gc.HasLen, 0)
   189  	} else {
   190  		c.Assert(actual, jc.SameContents, expect)
   191  	}
   192  }
   193  
   194  // collectChanges gets up to the max number of changes within the
   195  // testing.LongWait period.
   196  func (c StringsWatcherC) collectChanges(single bool, max int) []string {
   197  	c.State.StartSync()
   198  	timeout := time.After(testing.LongWait)
   199  	var actual []string
   200  	gotOneChange := false
   201  loop:
   202  	for {
   203  		select {
   204  		case changes, ok := <-c.Watcher.Changes():
   205  			c.Assert(ok, jc.IsTrue)
   206  			gotOneChange = true
   207  			actual = append(actual, changes...)
   208  			if single || len(actual) >= max {
   209  				break loop
   210  			}
   211  		case <-timeout:
   212  			if !gotOneChange {
   213  				c.Fatalf("watcher did not send change")
   214  			}
   215  		}
   216  	}
   217  	return actual
   218  }
   219  
   220  func (c StringsWatcherC) AssertClosed() {
   221  	select {
   222  	case _, ok := <-c.Watcher.Changes():
   223  		c.Assert(ok, jc.IsFalse)
   224  	default:
   225  		c.Fatalf("watcher not closed")
   226  	}
   227  }
   228  
   229  // RelationUnitsWatcherC embeds a gocheck.C and adds methods to help
   230  // verify the behaviour of any watcher that uses a <-chan
   231  // params.RelationUnitsChange.
   232  type RelationUnitsWatcherC struct {
   233  	*gc.C
   234  	State   SyncStarter
   235  	Watcher RelationUnitsWatcher
   236  	// settingsVersions keeps track of the settings version of each
   237  	// changed unit since the last received changes to ensure version
   238  	// always increases.
   239  	settingsVersions map[string]int64
   240  }
   241  
   242  // NewRelationUnitsWatcherC returns a RelationUnitsWatcherC that
   243  // checks for aggressive event coalescence.
   244  func NewRelationUnitsWatcherC(c *gc.C, st SyncStarter, w RelationUnitsWatcher) RelationUnitsWatcherC {
   245  	return RelationUnitsWatcherC{
   246  		C:                c,
   247  		State:            st,
   248  		Watcher:          w,
   249  		settingsVersions: make(map[string]int64),
   250  	}
   251  }
   252  
   253  type RelationUnitsWatcher interface {
   254  	Stop() error
   255  	Changes() <-chan params.RelationUnitsChange
   256  }
   257  
   258  func (c RelationUnitsWatcherC) AssertNoChange() {
   259  	c.State.StartSync()
   260  	select {
   261  	case actual, ok := <-c.Watcher.Changes():
   262  		c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok)
   263  	case <-time.After(testing.ShortWait):
   264  	}
   265  }
   266  
   267  // AssertChange asserts the given changes was reported by the watcher,
   268  // but does not assume there are no following changes.
   269  func (c RelationUnitsWatcherC) AssertChange(changed []string, departed []string) {
   270  	// Get all items in changed in a map for easy lookup.
   271  	changedNames := make(map[string]bool)
   272  	for _, name := range changed {
   273  		changedNames[name] = true
   274  	}
   275  	c.State.StartSync()
   276  	timeout := time.After(testing.LongWait)
   277  	select {
   278  	case actual, ok := <-c.Watcher.Changes():
   279  		c.Assert(ok, jc.IsTrue)
   280  		c.Assert(actual.Changed, gc.HasLen, len(changed))
   281  		// Because the versions can change, we only need to make sure
   282  		// the keys match, not the contents (UnitSettings == txnRevno).
   283  		for k, settings := range actual.Changed {
   284  			_, ok := changedNames[k]
   285  			c.Assert(ok, jc.IsTrue)
   286  			oldVer, ok := c.settingsVersions[k]
   287  			if !ok {
   288  				// This is the first time we see this unit, so
   289  				// save the settings version for later.
   290  				c.settingsVersions[k] = settings.Version
   291  			} else {
   292  				// Already seen; make sure the version increased.
   293  				if settings.Version <= oldVer {
   294  					c.Fatalf("expected unit settings version > %d (got %d)", oldVer, settings.Version)
   295  				}
   296  			}
   297  		}
   298  		c.Assert(actual.Departed, jc.SameContents, departed)
   299  	case <-timeout:
   300  		c.Fatalf("watcher did not send change")
   301  	}
   302  }
   303  
   304  func (c RelationUnitsWatcherC) AssertClosed() {
   305  	select {
   306  	case _, ok := <-c.Watcher.Changes():
   307  		c.Assert(ok, jc.IsFalse)
   308  	default:
   309  		c.Fatalf("watcher not closed")
   310  	}
   311  }