github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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/state"
    14  	"github.com/juju/juju/state/multiwatcher"
    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(), gc.IsNil)
    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   *state.State
    70  	Watcher NotifyWatcher
    71  }
    72  
    73  // NewNotifyWatcherC returns a NotifyWatcherC that checks for aggressive
    74  // event coalescence.
    75  func NewNotifyWatcherC(c *gc.C, st *state.State, w NotifyWatcher) NotifyWatcherC {
    76  	return NotifyWatcherC{
    77  		C:       c,
    78  		State:   st,
    79  		Watcher: w,
    80  	}
    81  }
    82  
    83  func (c NotifyWatcherC) AssertNoChange() {
    84  	c.State.StartSync()
    85  	select {
    86  	case _, ok := <-c.Watcher.Changes():
    87  		c.Fatalf("watcher sent unexpected change: (_, %v)", ok)
    88  	case <-time.After(testing.ShortWait):
    89  	}
    90  }
    91  
    92  func (c NotifyWatcherC) AssertOneChange() {
    93  	c.State.StartSync()
    94  	select {
    95  	case _, ok := <-c.Watcher.Changes():
    96  		c.Assert(ok, jc.IsTrue)
    97  	case <-time.After(testing.LongWait):
    98  		c.Fatalf("watcher did not send change")
    99  	}
   100  	c.AssertNoChange()
   101  }
   102  
   103  func (c NotifyWatcherC) AssertClosed() {
   104  	select {
   105  	case _, ok := <-c.Watcher.Changes():
   106  		c.Assert(ok, jc.IsFalse)
   107  	default:
   108  		c.Fatalf("watcher not closed")
   109  	}
   110  }
   111  
   112  // StringsWatcherC embeds a gocheck.C and adds methods to help verify
   113  // the behaviour of any watcher that uses a <-chan []string.
   114  type StringsWatcherC struct {
   115  	*gc.C
   116  	State   *state.State
   117  	Watcher StringsWatcher
   118  }
   119  
   120  // NewStringsWatcherC returns a StringsWatcherC that checks for aggressive
   121  // event coalescence.
   122  func NewStringsWatcherC(c *gc.C, st *state.State, w StringsWatcher) StringsWatcherC {
   123  	return StringsWatcherC{
   124  		C:       c,
   125  		State:   st,
   126  		Watcher: w,
   127  	}
   128  }
   129  
   130  type StringsWatcher interface {
   131  	Stop() error
   132  	Changes() <-chan []string
   133  }
   134  
   135  func (c StringsWatcherC) AssertNoChange() {
   136  	c.State.StartSync()
   137  	select {
   138  	case actual, ok := <-c.Watcher.Changes():
   139  		c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok)
   140  	case <-time.After(testing.ShortWait):
   141  	}
   142  }
   143  
   144  func (c StringsWatcherC) AssertChanges() {
   145  	c.State.StartSync()
   146  	select {
   147  	case <-c.Watcher.Changes():
   148  	case <-time.After(testing.LongWait):
   149  		c.Fatalf("watcher did not send change")
   150  	}
   151  }
   152  
   153  func (c StringsWatcherC) AssertChange(expect ...string) {
   154  	c.assertChange(false, expect...)
   155  }
   156  
   157  func (c StringsWatcherC) AssertChangeInSingleEvent(expect ...string) {
   158  	c.assertChange(true, expect...)
   159  }
   160  
   161  // AssertChangeMaybeIncluding verifies that there is a change that may
   162  // contain zero to all of the passed in strings, and no other changes.
   163  func (c StringsWatcherC) AssertChangeMaybeIncluding(expect ...string) {
   164  	maxCount := len(expect)
   165  	actual := c.collectChanges(true, maxCount)
   166  
   167  	if maxCount == 0 {
   168  		c.Assert(actual, gc.HasLen, 0)
   169  	} else {
   170  		actualCount := len(actual)
   171  		c.Assert(actualCount <= maxCount, jc.IsTrue, gc.Commentf("expected at most %d, got %d", maxCount, actualCount))
   172  		unexpected := set.NewStrings(actual...).Difference(set.NewStrings(expect...))
   173  		c.Assert(unexpected.Values(), gc.HasLen, 0)
   174  	}
   175  }
   176  
   177  // assertChange asserts the given list of changes was reported by
   178  // the watcher, but does not assume there are no following changes.
   179  func (c StringsWatcherC) assertChange(single bool, expect ...string) {
   180  	actual := c.collectChanges(single, len(expect))
   181  	if len(expect) == 0 {
   182  		c.Assert(actual, gc.HasLen, 0)
   183  	} else {
   184  		c.Assert(actual, jc.SameContents, expect)
   185  	}
   186  }
   187  
   188  // collectChanges gets up to the max number of changes within the
   189  // testing.LongWait period.
   190  func (c StringsWatcherC) collectChanges(single bool, max int) []string {
   191  	c.State.StartSync()
   192  	timeout := time.After(testing.LongWait)
   193  	var actual []string
   194  	gotOneChange := false
   195  loop:
   196  	for {
   197  		select {
   198  		case changes, ok := <-c.Watcher.Changes():
   199  			c.Assert(ok, jc.IsTrue)
   200  			gotOneChange = true
   201  			actual = append(actual, changes...)
   202  			if single || len(actual) >= max {
   203  				break loop
   204  			}
   205  		case <-timeout:
   206  			if !gotOneChange {
   207  				c.Fatalf("watcher did not send change")
   208  			}
   209  		}
   210  	}
   211  	return actual
   212  }
   213  
   214  func (c StringsWatcherC) AssertClosed() {
   215  	select {
   216  	case _, ok := <-c.Watcher.Changes():
   217  		c.Assert(ok, jc.IsFalse)
   218  	default:
   219  		c.Fatalf("watcher not closed")
   220  	}
   221  }
   222  
   223  // RelationUnitsWatcherC embeds a gocheck.C and adds methods to help
   224  // verify the behaviour of any watcher that uses a <-chan
   225  // params.RelationUnitsChange.
   226  type RelationUnitsWatcherC struct {
   227  	*gc.C
   228  	State   *state.State
   229  	Watcher RelationUnitsWatcher
   230  	// settingsVersions keeps track of the settings version of each
   231  	// changed unit since the last received changes to ensure version
   232  	// always increases.
   233  	settingsVersions map[string]int64
   234  }
   235  
   236  // NewRelationUnitsWatcherC returns a RelationUnitsWatcherC that
   237  // checks for aggressive event coalescence.
   238  func NewRelationUnitsWatcherC(c *gc.C, st *state.State, w RelationUnitsWatcher) RelationUnitsWatcherC {
   239  	return RelationUnitsWatcherC{
   240  		C:                c,
   241  		State:            st,
   242  		Watcher:          w,
   243  		settingsVersions: make(map[string]int64),
   244  	}
   245  }
   246  
   247  type RelationUnitsWatcher interface {
   248  	Stop() error
   249  	Changes() <-chan multiwatcher.RelationUnitsChange
   250  }
   251  
   252  func (c RelationUnitsWatcherC) AssertNoChange() {
   253  	c.State.StartSync()
   254  	select {
   255  	case actual, ok := <-c.Watcher.Changes():
   256  		c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok)
   257  	case <-time.After(testing.ShortWait):
   258  	}
   259  }
   260  
   261  // AssertChange asserts the given changes was reported by the watcher,
   262  // but does not assume there are no following changes.
   263  func (c RelationUnitsWatcherC) AssertChange(changed []string, departed []string) {
   264  	// Get all items in changed in a map for easy lookup.
   265  	changedNames := make(map[string]bool)
   266  	for _, name := range changed {
   267  		changedNames[name] = true
   268  	}
   269  	c.State.StartSync()
   270  	timeout := time.After(testing.LongWait)
   271  	select {
   272  	case actual, ok := <-c.Watcher.Changes():
   273  		c.Assert(ok, jc.IsTrue)
   274  		c.Assert(actual.Changed, gc.HasLen, len(changed))
   275  		// Because the versions can change, we only need to make sure
   276  		// the keys match, not the contents (UnitSettings == txnRevno).
   277  		for k, settings := range actual.Changed {
   278  			_, ok := changedNames[k]
   279  			c.Assert(ok, jc.IsTrue)
   280  			oldVer, ok := c.settingsVersions[k]
   281  			if !ok {
   282  				// This is the first time we see this unit, so
   283  				// save the settings version for later.
   284  				c.settingsVersions[k] = settings.Version
   285  			} else {
   286  				// Already seen; make sure the version increased.
   287  				if settings.Version <= oldVer {
   288  					c.Fatalf("expected unit settings version > %d (got %d)", oldVer, settings.Version)
   289  				}
   290  			}
   291  		}
   292  		c.Assert(actual.Departed, jc.SameContents, departed)
   293  	case <-timeout:
   294  		c.Fatalf("watcher did not send change")
   295  	}
   296  }
   297  
   298  func (c RelationUnitsWatcherC) AssertClosed() {
   299  	select {
   300  	case _, ok := <-c.Watcher.Changes():
   301  		c.Assert(ok, jc.IsFalse)
   302  	default:
   303  		c.Fatalf("watcher not closed")
   304  	}
   305  }