github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/uniter/hook/peeker_test.go (about)

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