github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/state/testing/watcher.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package testing
     5  
     6  import (
     7  	"sort"
     8  	"time"
     9  
    10  	gc "launchpad.net/gocheck"
    11  
    12  	"launchpad.net/juju-core/state"
    13  	"launchpad.net/juju-core/state/api/params"
    14  	"launchpad.net/juju-core/testing"
    15  	jc "launchpad.net/juju-core/testing/checkers"
    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  // AssertCanStopWhenSending ensures even when there are changes
    27  // pending to be delivered by the watcher it can still stop
    28  // cleanly. This is necessary to check for deadlocks in case the
    29  // watcher's inner loop is blocked trying to send and its tomb is
    30  // already dying.
    31  func AssertCanStopWhenSending(c *gc.C, stopper Stopper) {
    32  	// Leave some time for the event to be delivered and the watcher
    33  	// to block on sending it.
    34  	<-time.After(testing.ShortWait)
    35  	stopped := make(chan bool)
    36  	// Stop() blocks, so we need to call it in a separate goroutine.
    37  	go func() {
    38  		c.Check(stopper.Stop(), gc.IsNil)
    39  		stopped <- true
    40  	}()
    41  	select {
    42  	case <-time.After(testing.LongWait):
    43  		// NOTE: If this test fails here it means we have a deadlock
    44  		// in the client-side watcher implementation.
    45  		c.Fatalf("watcher did not stop as expected")
    46  	case <-stopped:
    47  	}
    48  }
    49  
    50  type NotifyWatcher interface {
    51  	Changes() <-chan struct{}
    52  }
    53  
    54  // NotifyWatcherC embeds a gocheck.C and adds methods to help verify
    55  // the behaviour of any watcher that uses a <-chan struct{}.
    56  type NotifyWatcherC struct {
    57  	*gc.C
    58  	State   *state.State
    59  	Watcher NotifyWatcher
    60  }
    61  
    62  // NewNotifyWatcherC returns a NotifyWatcherC that checks for aggressive
    63  // event coalescence.
    64  func NewNotifyWatcherC(c *gc.C, st *state.State, w NotifyWatcher) NotifyWatcherC {
    65  	return NotifyWatcherC{
    66  		C:       c,
    67  		State:   st,
    68  		Watcher: w,
    69  	}
    70  }
    71  
    72  func (c NotifyWatcherC) AssertNoChange() {
    73  	c.State.StartSync()
    74  	select {
    75  	case _, ok := <-c.Watcher.Changes():
    76  		c.Fatalf("watcher sent unexpected change: (_, %v)", ok)
    77  	case <-time.After(testing.ShortWait):
    78  	}
    79  }
    80  
    81  func (c NotifyWatcherC) AssertOneChange() {
    82  	c.State.StartSync()
    83  	select {
    84  	case _, ok := <-c.Watcher.Changes():
    85  		c.Assert(ok, jc.IsTrue)
    86  	case <-time.After(testing.LongWait):
    87  		c.Fatalf("watcher did not send change")
    88  	}
    89  	c.AssertNoChange()
    90  }
    91  
    92  func (c NotifyWatcherC) AssertClosed() {
    93  	select {
    94  	case _, ok := <-c.Watcher.Changes():
    95  		c.Assert(ok, jc.IsFalse)
    96  	default:
    97  		c.Fatalf("watcher not closed")
    98  	}
    99  }
   100  
   101  // StringsWatcherC embeds a gocheck.C and adds methods to help verify
   102  // the behaviour of any watcher that uses a <-chan []string.
   103  type StringsWatcherC struct {
   104  	*gc.C
   105  	State   *state.State
   106  	Watcher StringsWatcher
   107  }
   108  
   109  // NewStringsWatcherC returns a StringsWatcherC that checks for aggressive
   110  // event coalescence.
   111  func NewStringsWatcherC(c *gc.C, st *state.State, w StringsWatcher) StringsWatcherC {
   112  	return StringsWatcherC{
   113  		C:       c,
   114  		State:   st,
   115  		Watcher: w,
   116  	}
   117  }
   118  
   119  type StringsWatcher interface {
   120  	Stop() error
   121  	Changes() <-chan []string
   122  }
   123  
   124  func (c StringsWatcherC) AssertNoChange() {
   125  	c.State.StartSync()
   126  	select {
   127  	case actual, ok := <-c.Watcher.Changes():
   128  		c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok)
   129  	case <-time.After(testing.ShortWait):
   130  	}
   131  }
   132  
   133  func (c StringsWatcherC) AssertChange(expect ...string) {
   134  	c.assertChange(false, expect...)
   135  }
   136  
   137  func (c StringsWatcherC) AssertChangeInSingleEvent(expect ...string) {
   138  	c.assertChange(true, expect...)
   139  }
   140  
   141  // AssertChange asserts the given list of changes was reported by
   142  // the watcher, but does not assume there are no following changes.
   143  func (c StringsWatcherC) assertChange(single bool, expect ...string) {
   144  	c.State.StartSync()
   145  	timeout := time.After(testing.LongWait)
   146  	var actual []string
   147  loop:
   148  	for {
   149  		select {
   150  		case changes, ok := <-c.Watcher.Changes():
   151  			c.Assert(ok, jc.IsTrue)
   152  			actual = append(actual, changes...)
   153  			if single || len(actual) >= len(expect) {
   154  				break loop
   155  			}
   156  		case <-timeout:
   157  			c.Fatalf("watcher did not send change")
   158  		}
   159  	}
   160  	if len(expect) == 0 {
   161  		c.Assert(actual, gc.HasLen, 0)
   162  	} else {
   163  		sort.Strings(expect)
   164  		sort.Strings(actual)
   165  		c.Assert(actual, gc.DeepEquals, expect)
   166  	}
   167  }
   168  
   169  func (c StringsWatcherC) AssertClosed() {
   170  	select {
   171  	case _, ok := <-c.Watcher.Changes():
   172  		c.Assert(ok, jc.IsFalse)
   173  	default:
   174  		c.Fatalf("watcher not closed")
   175  	}
   176  }
   177  
   178  // RelationUnitsWatcherC embeds a gocheck.C and adds methods to help
   179  // verify the behaviour of any watcher that uses a <-chan
   180  // params.RelationUnitsChange.
   181  type RelationUnitsWatcherC struct {
   182  	*gc.C
   183  	State   *state.State
   184  	Watcher RelationUnitsWatcher
   185  	// settingsVersions keeps track of the settings version of each
   186  	// changed unit since the last received changes to ensure version
   187  	// always increases.
   188  	settingsVersions map[string]int64
   189  }
   190  
   191  // NewRelationUnitsWatcherC returns a RelationUnitsWatcherC that
   192  // checks for aggressive event coalescence.
   193  func NewRelationUnitsWatcherC(c *gc.C, st *state.State, w RelationUnitsWatcher) RelationUnitsWatcherC {
   194  	return RelationUnitsWatcherC{
   195  		C:                c,
   196  		State:            st,
   197  		Watcher:          w,
   198  		settingsVersions: make(map[string]int64),
   199  	}
   200  }
   201  
   202  type RelationUnitsWatcher interface {
   203  	Stop() error
   204  	Changes() <-chan params.RelationUnitsChange
   205  }
   206  
   207  func (c RelationUnitsWatcherC) AssertNoChange() {
   208  	c.State.StartSync()
   209  	select {
   210  	case actual, ok := <-c.Watcher.Changes():
   211  		c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok)
   212  	case <-time.After(testing.ShortWait):
   213  	}
   214  }
   215  
   216  // AssertChange asserts the given changes was reported by the watcher,
   217  // but does not assume there are no following changes.
   218  func (c RelationUnitsWatcherC) AssertChange(changed []string, departed []string) {
   219  	// Get all items in changed in a map for easy lookup.
   220  	changedNames := make(map[string]bool)
   221  	for _, name := range changed {
   222  		changedNames[name] = true
   223  	}
   224  	c.State.StartSync()
   225  	timeout := time.After(testing.LongWait)
   226  	select {
   227  	case actual, ok := <-c.Watcher.Changes():
   228  		c.Assert(ok, jc.IsTrue)
   229  		c.Assert(actual.Changed, gc.HasLen, len(changed))
   230  		// Because the versions can change, we only need to make sure
   231  		// the keys match, not the contents (UnitSettings == txnRevno).
   232  		for k, settings := range actual.Changed {
   233  			_, ok := changedNames[k]
   234  			c.Assert(ok, jc.IsTrue)
   235  			oldVer, ok := c.settingsVersions[k]
   236  			if !ok {
   237  				// This is the first time we see this unit, so
   238  				// save the settings version for later.
   239  				c.settingsVersions[k] = settings.Version
   240  			} else {
   241  				// Already seen; make sure the version increased.
   242  				if settings.Version <= oldVer {
   243  					c.Fatalf("expected unit settings version > %d (got %d)", oldVer, settings.Version)
   244  				}
   245  			}
   246  		}
   247  		c.Assert(actual.Departed, jc.SameContents, departed)
   248  	case <-timeout:
   249  		c.Fatalf("watcher did not send change")
   250  	}
   251  }
   252  
   253  func (c RelationUnitsWatcherC) AssertClosed() {
   254  	select {
   255  	case _, ok := <-c.Watcher.Changes():
   256  		c.Assert(ok, jc.IsFalse)
   257  	default:
   258  		c.Fatalf("watcher not closed")
   259  	}
   260  }