github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/relation/peeker_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package relation_test
     5  
     6  import (
     7  	"time"
     8  
     9  	jc "github.com/juju/testing/checkers"
    10  	gc "gopkg.in/check.v1"
    11  	"gopkg.in/juju/charm.v4/hooks"
    12  
    13  	"github.com/juju/juju/state/multiwatcher"
    14  	statetesting "github.com/juju/juju/state/testing"
    15  	coretesting "github.com/juju/juju/testing"
    16  	"github.com/juju/juju/worker/uniter/hook"
    17  	"github.com/juju/juju/worker/uniter/relation"
    18  )
    19  
    20  type PeekerSuite struct {
    21  }
    22  
    23  var _ = gc.Suite(&PeekerSuite{})
    24  
    25  func (s *PeekerSuite) TestPeeks(c *gc.C) {
    26  	expect := hookList(hooks.Install, hooks.ConfigChanged, hooks.Start)
    27  	source := relation.NewListSource(expect)
    28  	peeker := relation.NewPeeker(source)
    29  	defer statetesting.AssertStop(c, peeker)
    30  
    31  	timeout := time.After(coretesting.LongWait)
    32  	for _, expectInfo := range expect {
    33  		select {
    34  		case peek, ok := <-peeker.Peeks():
    35  			c.Assert(ok, jc.IsTrue)
    36  			c.Assert(peek.HookInfo(), gc.Equals, expectInfo)
    37  			peek.Reject()
    38  		case <-timeout:
    39  			c.Fatalf("ran out of time")
    40  		}
    41  		select {
    42  		case peek, ok := <-peeker.Peeks():
    43  			c.Assert(ok, jc.IsTrue)
    44  			c.Assert(peek.HookInfo(), gc.Equals, expectInfo)
    45  			peek.Consume()
    46  		case <-timeout:
    47  			c.Fatalf("ran out of time")
    48  		}
    49  	}
    50  	select {
    51  	case <-time.After(coretesting.ShortWait):
    52  	case peek, ok := <-peeker.Peeks():
    53  		c.Fatalf("unexpected peek from empty queue: %#v, %#v", peek, ok)
    54  	}
    55  }
    56  
    57  func (s *PeekerSuite) TestStopWhileRunning(c *gc.C) {
    58  	source := newFullUnbufferedSource()
    59  	defer statetesting.AssertStop(c, source)
    60  
    61  	peeker := relation.NewPeeker(source)
    62  	defer statetesting.AssertStop(c, peeker)
    63  
    64  	// Grab and reject a peek to check we're running.
    65  	select {
    66  	case peek, ok := <-peeker.Peeks():
    67  		c.Assert(ok, jc.IsTrue)
    68  		c.Assert(peek.HookInfo(), gc.Equals, hook.Info{Kind: hooks.Install})
    69  		peek.Reject()
    70  		assertStopPeeker(c, peeker, source)
    71  	case <-time.After(coretesting.LongWait):
    72  		c.Fatalf("timed out")
    73  	}
    74  }
    75  
    76  func (s *PeekerSuite) TestStopWhilePeeking(c *gc.C) {
    77  	source := newFullUnbufferedSource()
    78  	defer statetesting.AssertStop(c, source)
    79  
    80  	peeker := relation.NewPeeker(source)
    81  	defer statetesting.AssertStop(c, peeker)
    82  
    83  	select {
    84  	case peek, ok := <-peeker.Peeks():
    85  		c.Assert(ok, jc.IsTrue)
    86  		c.Assert(peek.HookInfo(), gc.Equals, hook.Info{Kind: hooks.Install})
    87  		assertStopPeeker(c, peeker, source)
    88  	case <-time.After(coretesting.LongWait):
    89  		c.Fatalf("timed out")
    90  	}
    91  }
    92  
    93  func (s *PeekerSuite) TestStopWhileUpdating(c *gc.C) {
    94  	source := newFullBufferedSource()
    95  	defer statetesting.AssertStop(c, source)
    96  
    97  	peeker := relation.NewPeeker(source)
    98  	defer statetesting.AssertStop(c, peeker)
    99  
   100  	// Deliver a change; do not accept updates.
   101  	select {
   102  	case source.changes <- multiwatcher.RelationUnitsChange{}:
   103  		assertStopPeeker(c, peeker, source)
   104  	case <-time.After(coretesting.LongWait):
   105  		c.Fatalf("timed out")
   106  	}
   107  }
   108  
   109  func (s *PeekerSuite) TestUpdatesFullQueue(c *gc.C) {
   110  	source := newFullUnbufferedSource()
   111  	defer statetesting.AssertStop(c, source)
   112  
   113  	peeker := relation.NewPeeker(source)
   114  	defer statetesting.AssertStop(c, peeker)
   115  
   116  	// Check we're being sent peeks but not updates.
   117  	timeout := time.After(coretesting.LongWait)
   118  	assertActive := func() {
   119  		select {
   120  		case peek, ok := <-peeker.Peeks():
   121  			c.Assert(ok, jc.IsTrue)
   122  			defer peek.Reject()
   123  			c.Assert(peek.HookInfo(), gc.Equals, hook.Info{Kind: hooks.Install})
   124  		case update, ok := <-source.updates:
   125  			c.Fatalf("got unexpected update: %#v %#v", update, ok)
   126  		case <-timeout:
   127  			c.Fatalf("timed out")
   128  		}
   129  	}
   130  	assertActive()
   131  
   132  	// Send an event on the Changes() chan.
   133  	sent := multiwatcher.RelationUnitsChange{Departed: []string{"sent"}}
   134  	select {
   135  	case source.changes <- sent:
   136  	case <-timeout:
   137  		c.Fatalf("could not send change")
   138  	}
   139  
   140  	// Now that a change has been delivered, nothing should be sent on the out
   141  	// chan, or read from the changes chan, until the Update method has completed.
   142  	notSent := multiwatcher.RelationUnitsChange{Departed: []string{"notSent"}}
   143  	select {
   144  	case source.changes <- notSent:
   145  		c.Fatalf("sent extra change while updating queue")
   146  	case peek, ok := <-peeker.Peeks():
   147  		c.Fatalf("got unexpected peek while updating queue: %#v %#v", peek, ok)
   148  	case got, ok := <-source.updates:
   149  		c.Assert(ok, jc.IsTrue)
   150  		c.Assert(got, gc.DeepEquals, sent)
   151  	case <-timeout:
   152  		c.Fatalf("timed out")
   153  	}
   154  
   155  	// Check we're still being sent hooks and not updates.
   156  	assertActive()
   157  }
   158  
   159  func (s *PeekerSuite) TestUpdatesFullQueueSpam(c *gc.C) {
   160  	source := newFullUnbufferedSource()
   161  	defer statetesting.AssertStop(c, source)
   162  
   163  	peeker := relation.NewPeeker(source)
   164  	defer statetesting.AssertStop(c, peeker)
   165  
   166  	// Spam all channels continuously for a bit.
   167  	timeout := time.After(coretesting.LongWait)
   168  	peekCount := 0
   169  	changeCount := 0
   170  	updateCount := 0
   171  	for i := 0; i < 100; i++ {
   172  		select {
   173  		case peek, ok := <-peeker.Peeks():
   174  			c.Assert(ok, jc.IsTrue)
   175  			c.Assert(peek.HookInfo(), gc.DeepEquals, hook.Info{Kind: hooks.Install})
   176  			peek.Consume()
   177  			peekCount++
   178  		case source.changes <- multiwatcher.RelationUnitsChange{}:
   179  			changeCount++
   180  		case update, ok := <-source.updates:
   181  			c.Assert(ok, jc.IsTrue)
   182  			c.Assert(update, gc.DeepEquals, multiwatcher.RelationUnitsChange{})
   183  			updateCount++
   184  		case <-timeout:
   185  			c.Fatalf("not enough things happened in time")
   186  		}
   187  	}
   188  
   189  	// Once we've finished sending, exhaust the updates...
   190  	for i := updateCount; i < changeCount && updateCount < changeCount; i++ {
   191  		select {
   192  		case update, ok := <-source.updates:
   193  			c.Assert(ok, jc.IsTrue)
   194  			c.Assert(update, gc.DeepEquals, multiwatcher.RelationUnitsChange{})
   195  			updateCount++
   196  		case <-timeout:
   197  			c.Fatalf("expected %d updates, got %d", changeCount, updateCount)
   198  		}
   199  	}
   200  
   201  	// ...and check sane end state to validate the foregoing.
   202  	c.Check(peekCount, gc.Not(gc.Equals), 0)
   203  	c.Check(changeCount, gc.Not(gc.Equals), 0)
   204  }
   205  
   206  func (s *PeekerSuite) TestUpdatesEmptyQueue(c *gc.C) {
   207  	source := newEmptySource()
   208  	defer statetesting.AssertStop(c, source)
   209  
   210  	peeker := relation.NewPeeker(source)
   211  	defer statetesting.AssertStop(c, peeker)
   212  
   213  	// Check no hooks are sent and no updates delivered.
   214  	assertIdle := func() {
   215  		select {
   216  		case peek, ok := <-peeker.Peeks():
   217  			c.Fatalf("got unexpected peek: %#v %#v", peek, ok)
   218  		case update, ok := <-source.updates:
   219  			c.Fatalf("got unexpected update: %#v %#v", update, ok)
   220  		case <-time.After(coretesting.ShortWait):
   221  		}
   222  	}
   223  	assertIdle()
   224  
   225  	// Send an event on the Changes() chan.
   226  	sent := multiwatcher.RelationUnitsChange{Departed: []string{"sent"}}
   227  	timeout := time.After(coretesting.LongWait)
   228  	select {
   229  	case source.changes <- sent:
   230  	case <-timeout:
   231  		c.Fatalf("timed out")
   232  	}
   233  
   234  	// Now that a change has been delivered, nothing should be sent on the out
   235  	// chan, or read from the changes chan, until the Update method has completed.
   236  	notSent := multiwatcher.RelationUnitsChange{Departed: []string{"notSent"}}
   237  	select {
   238  	case source.changes <- notSent:
   239  		c.Fatalf("sent extra change while updating queue")
   240  	case peek, ok := <-peeker.Peeks():
   241  		c.Fatalf("got unexpected peek while updating queue: %#v %#v", peek, ok)
   242  	case got, ok := <-source.updates:
   243  		c.Assert(ok, jc.IsTrue)
   244  		c.Assert(got, gc.DeepEquals, sent)
   245  	case <-timeout:
   246  		c.Fatalf("timed out")
   247  	}
   248  
   249  	// Now the change has been delivered, nothing should be happening.
   250  	assertIdle()
   251  }
   252  
   253  func (s *PeekerSuite) TestUpdatesEmptyQueueSpam(c *gc.C) {
   254  	source := newEmptySource()
   255  	defer statetesting.AssertStop(c, source)
   256  
   257  	peeker := relation.NewPeeker(source)
   258  	defer statetesting.AssertStop(c, peeker)
   259  
   260  	// Spam all channels continuously for a bit.
   261  	timeout := time.After(coretesting.LongWait)
   262  	changeCount := 0
   263  	updateCount := 0
   264  	for i := 0; i < 100; i++ {
   265  		select {
   266  		case peek, ok := <-peeker.Peeks():
   267  			c.Fatalf("got unexpected peek: %#v %#v", peek, ok)
   268  		case source.changes <- multiwatcher.RelationUnitsChange{}:
   269  			changeCount++
   270  		case update, ok := <-source.updates:
   271  			c.Assert(ok, jc.IsTrue)
   272  			c.Assert(update, gc.DeepEquals, multiwatcher.RelationUnitsChange{})
   273  			updateCount++
   274  		case <-timeout:
   275  			c.Fatalf("not enough things happened in time")
   276  		}
   277  	}
   278  
   279  	// Check sane end state.
   280  	c.Check(changeCount, gc.Equals, 50)
   281  	c.Check(updateCount, gc.Equals, 50)
   282  }
   283  func (s *PeekerSuite) TestPeeksBlockUntilRejected(c *gc.C) {
   284  	source := newFullBufferedSource()
   285  	defer statetesting.AssertStop(c, source)
   286  
   287  	peeker := relation.NewPeeker(source)
   288  	defer statetesting.AssertStop(c, peeker)
   289  
   290  	// Collect a peek...
   291  	timeout := time.After(coretesting.LongWait)
   292  	select {
   293  	case <-timeout:
   294  		c.Fatalf("failed to receive peek")
   295  	case peek, ok := <-peeker.Peeks():
   296  		c.Assert(ok, jc.IsTrue)
   297  		c.Assert(peek.HookInfo(), gc.Equals, hook.Info{Kind: hooks.Install})
   298  
   299  		// ...and check that changes can't be delivered...
   300  		select {
   301  		case source.changes <- multiwatcher.RelationUnitsChange{}:
   302  			c.Fatalf("delivered change while supposedly peeking")
   303  		default:
   304  		}
   305  
   306  		// ...before the peek is rejected, at which point changes are unblocked.
   307  		peek.Reject()
   308  		select {
   309  		case source.changes <- multiwatcher.RelationUnitsChange{}:
   310  		case <-timeout:
   311  			c.Fatalf("failed to unblock changes")
   312  		}
   313  	}
   314  }
   315  
   316  func (s *PeekerSuite) TestPeeksBlockUntilConsumed(c *gc.C) {
   317  	source := newFullBufferedSource()
   318  	defer statetesting.AssertStop(c, source)
   319  
   320  	peeker := relation.NewPeeker(source)
   321  	defer statetesting.AssertStop(c, peeker)
   322  
   323  	// Collect a peek...
   324  	timeout := time.After(coretesting.LongWait)
   325  	select {
   326  	case <-timeout:
   327  		c.Fatalf("failed to receive peek")
   328  	case peek, ok := <-peeker.Peeks():
   329  		c.Assert(ok, jc.IsTrue)
   330  		c.Assert(peek.HookInfo(), gc.Equals, hook.Info{Kind: hooks.Install})
   331  
   332  		// ...and check that changes can't be delivered...
   333  		select {
   334  		case source.changes <- multiwatcher.RelationUnitsChange{}:
   335  			c.Fatalf("delivered change while supposedly peeking")
   336  		default:
   337  		}
   338  
   339  		// ...before the peek is consumed, at which point changes are unblocked.
   340  		peek.Consume()
   341  		select {
   342  		case source.changes <- multiwatcher.RelationUnitsChange{}:
   343  		case <-timeout:
   344  			c.Fatalf("failed to unblock changes")
   345  		}
   346  	}
   347  }
   348  
   349  func (s *PeekerSuite) TestBlocksWhenUpdating(c *gc.C) {
   350  	source := newFullUnbufferedSource()
   351  	defer statetesting.AssertStop(c, source)
   352  
   353  	peeker := relation.NewPeeker(source)
   354  	defer statetesting.AssertStop(c, peeker)
   355  
   356  	// Deliver a change.
   357  	timeout := time.After(coretesting.LongWait)
   358  	select {
   359  	case source.changes <- multiwatcher.RelationUnitsChange{}:
   360  	case <-timeout:
   361  		c.Fatalf("failed to send change")
   362  	}
   363  
   364  	// Check peeks are not delivered...
   365  	select {
   366  	case peek, ok := <-peeker.Peeks():
   367  		c.Fatalf("got unexpected peek: %#v, %#v", peek, ok)
   368  	default:
   369  	}
   370  
   371  	// ...before the update is handled...
   372  	select {
   373  	case update, ok := <-source.updates:
   374  		c.Assert(ok, jc.IsTrue)
   375  		c.Assert(update, gc.DeepEquals, multiwatcher.RelationUnitsChange{})
   376  	case <-timeout:
   377  		c.Fatalf("failed to collect update")
   378  	}
   379  
   380  	// ...at which point peeks are expected again.
   381  	select {
   382  	case peek, ok := <-peeker.Peeks():
   383  		c.Assert(ok, jc.IsTrue)
   384  		c.Assert(peek.HookInfo(), gc.Equals, hook.Info{Kind: hooks.Install})
   385  		peek.Reject()
   386  	case <-timeout:
   387  		c.Fatalf("failed to send peek")
   388  	}
   389  }
   390  
   391  func assertStopPeeker(c *gc.C, peeker relation.Peeker, source *updateSource) {
   392  	// Stop the peeker...
   393  	err := peeker.Stop()
   394  	c.Assert(err, gc.IsNil)
   395  
   396  	// ...and check it closed the channel...
   397  	select {
   398  	case peek, ok := <-peeker.Peeks():
   399  		c.Assert(peek, gc.IsNil)
   400  		c.Assert(ok, jc.IsFalse)
   401  	default:
   402  		c.Fatalf("peeks channel not closed")
   403  	}
   404  
   405  	// ...and stopped the source.
   406  	select {
   407  	case <-source.tomb.Dead():
   408  		c.Assert(source.tomb.Err(), jc.ErrorIsNil)
   409  	default:
   410  		c.Fatalf("source not stopped")
   411  	}
   412  }