github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/watcher/legacy/notifyworker_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package legacy_test
     5  
     6  import (
     7  	"fmt"
     8  	"sync"
     9  	"time"
    10  
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  	"launchpad.net/tomb"
    14  
    15  	"github.com/juju/juju/state"
    16  	"github.com/juju/juju/state/watcher"
    17  	coretesting "github.com/juju/juju/testing"
    18  	"github.com/juju/juju/watcher/legacy"
    19  	"github.com/juju/juju/worker"
    20  )
    21  
    22  type NotifyWorkerSuite struct {
    23  	coretesting.BaseSuite
    24  	worker worker.Worker
    25  	actor  *notifyHandler
    26  }
    27  
    28  var _ = gc.Suite(&NotifyWorkerSuite{})
    29  
    30  func newNotifyHandlerWorker(c *gc.C, setupError, handlerError, teardownError error) (*notifyHandler, worker.Worker) {
    31  	nh := &notifyHandler{
    32  		actions:       nil,
    33  		handled:       make(chan struct{}, 1),
    34  		setupError:    setupError,
    35  		teardownError: teardownError,
    36  		handlerError:  handlerError,
    37  		watcher: &testNotifyWatcher{
    38  			changes: make(chan struct{}),
    39  		},
    40  		setupDone: make(chan struct{}),
    41  	}
    42  	w := legacy.NewNotifyWorker(nh)
    43  	select {
    44  	case <-nh.setupDone:
    45  	case <-time.After(coretesting.ShortWait):
    46  		c.Error("Failed waiting for notifyHandler.Setup to be called during SetUpTest")
    47  	}
    48  	return nh, w
    49  }
    50  
    51  func (s *NotifyWorkerSuite) SetUpTest(c *gc.C) {
    52  	s.BaseSuite.SetUpTest(c)
    53  	s.actor, s.worker = newNotifyHandlerWorker(c, nil, nil, nil)
    54  }
    55  
    56  func (s *NotifyWorkerSuite) TearDownTest(c *gc.C) {
    57  	legacy.SetEnsureErr(nil)
    58  	s.stopWorker(c)
    59  	s.BaseSuite.TearDownTest(c)
    60  }
    61  
    62  type notifyHandler struct {
    63  	actions       []string
    64  	mu            sync.Mutex
    65  	handled       chan struct{}
    66  	setupError    error
    67  	teardownError error
    68  	handlerError  error
    69  	watcher       *testNotifyWatcher
    70  	setupDone     chan struct{}
    71  }
    72  
    73  func (nh *notifyHandler) SetUp() (state.NotifyWatcher, error) {
    74  	defer func() { nh.setupDone <- struct{}{} }()
    75  	nh.mu.Lock()
    76  	defer nh.mu.Unlock()
    77  	nh.actions = append(nh.actions, "setup")
    78  	if nh.watcher == nil {
    79  		return nil, nh.setupError
    80  	}
    81  	return nh.watcher, nh.setupError
    82  }
    83  
    84  func (nh *notifyHandler) TearDown() error {
    85  	nh.mu.Lock()
    86  	defer nh.mu.Unlock()
    87  	nh.actions = append(nh.actions, "teardown")
    88  	if nh.handled != nil {
    89  		close(nh.handled)
    90  	}
    91  	return nh.teardownError
    92  }
    93  
    94  func (nh *notifyHandler) Handle(_ <-chan struct{}) error {
    95  	nh.mu.Lock()
    96  	defer nh.mu.Unlock()
    97  	nh.actions = append(nh.actions, "handler")
    98  	if nh.handled != nil {
    99  		// Unlock while we are waiting for the send
   100  		nh.mu.Unlock()
   101  		nh.handled <- struct{}{}
   102  		nh.mu.Lock()
   103  	}
   104  	return nh.handlerError
   105  }
   106  
   107  func (nh *notifyHandler) CheckActions(c *gc.C, actions ...string) {
   108  	nh.mu.Lock()
   109  	defer nh.mu.Unlock()
   110  	c.Check(nh.actions, gc.DeepEquals, actions)
   111  }
   112  
   113  // During teardown we try to stop the worker, but don't hang the test suite if
   114  // Stop never returns
   115  func (s *NotifyWorkerSuite) stopWorker(c *gc.C) {
   116  	if s.worker == nil {
   117  		return
   118  	}
   119  	done := make(chan error)
   120  	go func() {
   121  		done <- worker.Stop(s.worker)
   122  	}()
   123  	err := waitForTimeout(c, done, coretesting.LongWait)
   124  	c.Check(err, jc.ErrorIsNil)
   125  	s.actor = nil
   126  	s.worker = nil
   127  }
   128  
   129  type testNotifyWatcher struct {
   130  	state.NotifyWatcher
   131  	mu        sync.Mutex
   132  	changes   chan struct{}
   133  	stopped   bool
   134  	stopError error
   135  }
   136  
   137  func (tnw *testNotifyWatcher) Changes() <-chan struct{} {
   138  	return tnw.changes
   139  }
   140  
   141  func (tnw *testNotifyWatcher) Err() error {
   142  	return tnw.stopError
   143  }
   144  
   145  func (tnw *testNotifyWatcher) Stop() error {
   146  	tnw.mu.Lock()
   147  	defer tnw.mu.Unlock()
   148  	if !tnw.stopped {
   149  		close(tnw.changes)
   150  	}
   151  	tnw.stopped = true
   152  	return tnw.stopError
   153  }
   154  
   155  func (tnw *testNotifyWatcher) SetStopError(err error) {
   156  	tnw.mu.Lock()
   157  	tnw.stopError = err
   158  	tnw.mu.Unlock()
   159  }
   160  
   161  func (tnw *testNotifyWatcher) TriggerChange(c *gc.C) {
   162  	select {
   163  	case tnw.changes <- struct{}{}:
   164  	case <-time.After(coretesting.LongWait):
   165  		c.Errorf("timed out trying to trigger a change")
   166  	}
   167  }
   168  
   169  func waitForTimeout(c *gc.C, ch <-chan error, timeout time.Duration) error {
   170  	select {
   171  	case err := <-ch:
   172  		return err
   173  	case <-time.After(timeout):
   174  		c.Errorf("timed out waiting to receive a change after %s", timeout)
   175  	}
   176  	return nil
   177  }
   178  
   179  func waitShort(c *gc.C, w worker.Worker) error {
   180  	done := make(chan error)
   181  	go func() {
   182  		done <- w.Wait()
   183  	}()
   184  	return waitForTimeout(c, done, coretesting.ShortWait)
   185  }
   186  
   187  func waitForHandledNotify(c *gc.C, handled chan struct{}) {
   188  	select {
   189  	case <-handled:
   190  	case <-time.After(coretesting.LongWait):
   191  		c.Errorf("handled failed to signal after %s", coretesting.LongWait)
   192  	}
   193  }
   194  
   195  func (s *NotifyWorkerSuite) TestKill(c *gc.C) {
   196  	s.worker.Kill()
   197  	err := waitShort(c, s.worker)
   198  	c.Assert(err, jc.ErrorIsNil)
   199  }
   200  
   201  func (s *NotifyWorkerSuite) TestStop(c *gc.C) {
   202  	err := worker.Stop(s.worker)
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	// After stop, Wait should return right away
   205  	err = waitShort(c, s.worker)
   206  	c.Assert(err, jc.ErrorIsNil)
   207  }
   208  
   209  func (s *NotifyWorkerSuite) TestWait(c *gc.C) {
   210  	done := make(chan error)
   211  	go func() {
   212  		done <- s.worker.Wait()
   213  	}()
   214  	// Wait should not return until we've killed the worker
   215  	select {
   216  	case err := <-done:
   217  		c.Errorf("Wait() didn't wait until we stopped it: %v", err)
   218  	case <-time.After(coretesting.ShortWait):
   219  	}
   220  	s.worker.Kill()
   221  	err := waitForTimeout(c, done, coretesting.LongWait)
   222  	c.Assert(err, jc.ErrorIsNil)
   223  }
   224  
   225  func (s *NotifyWorkerSuite) TestCallSetUpAndTearDown(c *gc.C) {
   226  	// After calling NewNotifyWorker, we should have called setup
   227  	s.actor.CheckActions(c, "setup")
   228  	// If we kill the worker, it should notice, and call teardown
   229  	s.worker.Kill()
   230  	err := waitShort(c, s.worker)
   231  	c.Check(err, jc.ErrorIsNil)
   232  	s.actor.CheckActions(c, "setup", "teardown")
   233  	c.Check(s.actor.watcher.stopped, jc.IsTrue)
   234  }
   235  
   236  func (s *NotifyWorkerSuite) TestChangesTriggerHandler(c *gc.C) {
   237  	s.actor.CheckActions(c, "setup")
   238  	s.actor.watcher.TriggerChange(c)
   239  	waitForHandledNotify(c, s.actor.handled)
   240  	s.actor.CheckActions(c, "setup", "handler")
   241  	s.actor.watcher.TriggerChange(c)
   242  	waitForHandledNotify(c, s.actor.handled)
   243  	s.actor.watcher.TriggerChange(c)
   244  	waitForHandledNotify(c, s.actor.handled)
   245  	s.actor.CheckActions(c, "setup", "handler", "handler", "handler")
   246  	c.Assert(worker.Stop(s.worker), gc.IsNil)
   247  	s.actor.CheckActions(c, "setup", "handler", "handler", "handler", "teardown")
   248  }
   249  
   250  func (s *NotifyWorkerSuite) TestSetUpFailureStopsWithTearDown(c *gc.C) {
   251  	// Stop the worker and SetUp again, this time with an error
   252  	s.stopWorker(c)
   253  	actor, w := newNotifyHandlerWorker(c, fmt.Errorf("my special error"), nil, nil)
   254  	err := waitShort(c, w)
   255  	c.Check(err, gc.ErrorMatches, "my special error")
   256  	// TearDown is not called on SetUp error.
   257  	actor.CheckActions(c, "setup")
   258  	c.Check(actor.watcher.stopped, jc.IsTrue)
   259  }
   260  
   261  func (s *NotifyWorkerSuite) TestWatcherStopFailurePropagates(c *gc.C) {
   262  	s.actor.watcher.SetStopError(fmt.Errorf("error while stopping watcher"))
   263  	s.worker.Kill()
   264  	c.Assert(s.worker.Wait(), gc.ErrorMatches, "error while stopping watcher")
   265  	// We've already stopped the worker, don't let teardown notice the
   266  	// worker is in an error state
   267  	s.worker = nil
   268  }
   269  
   270  func (s *NotifyWorkerSuite) TestCleanRunNoticesTearDownError(c *gc.C) {
   271  	s.actor.teardownError = fmt.Errorf("failed to tear down watcher")
   272  	s.worker.Kill()
   273  	c.Assert(s.worker.Wait(), gc.ErrorMatches, "failed to tear down watcher")
   274  	s.worker = nil
   275  }
   276  
   277  func (s *NotifyWorkerSuite) TestHandleErrorStopsWorkerAndWatcher(c *gc.C) {
   278  	s.stopWorker(c)
   279  	actor, w := newNotifyHandlerWorker(c, nil, fmt.Errorf("my handling error"), nil)
   280  	actor.watcher.TriggerChange(c)
   281  	waitForHandledNotify(c, actor.handled)
   282  	err := waitShort(c, w)
   283  	c.Check(err, gc.ErrorMatches, "my handling error")
   284  	actor.CheckActions(c, "setup", "handler", "teardown")
   285  	c.Check(actor.watcher.stopped, jc.IsTrue)
   286  }
   287  
   288  func (s *NotifyWorkerSuite) TestNoticesStoppedWatcher(c *gc.C) {
   289  	// The default closedHandler doesn't panic if you have a genuine error
   290  	// (because it assumes you want to propagate a real error and then
   291  	// restart
   292  	s.actor.watcher.SetStopError(fmt.Errorf("Stopped Watcher"))
   293  	s.actor.watcher.Stop()
   294  	err := waitShort(c, s.worker)
   295  	c.Check(err, gc.ErrorMatches, "Stopped Watcher")
   296  	s.actor.CheckActions(c, "setup", "teardown")
   297  	// Worker is stopped, don't fail TearDownTest
   298  	s.worker = nil
   299  }
   300  
   301  func noopHandler(watcher.Errer) error {
   302  	return nil
   303  }
   304  
   305  type CannedErrer struct {
   306  	err error
   307  }
   308  
   309  func (c CannedErrer) Err() error {
   310  	return c.err
   311  }
   312  
   313  func (s *NotifyWorkerSuite) TestDefaultClosedHandler(c *gc.C) {
   314  	// Roundabout check for function equality.
   315  	// Is this test really worth it?
   316  	c.Assert(fmt.Sprintf("%p", legacy.EnsureErr()), gc.Equals, fmt.Sprintf("%p", watcher.EnsureErr))
   317  }
   318  
   319  func (s *NotifyWorkerSuite) TestErrorsOnStillAliveButClosedChannel(c *gc.C) {
   320  	foundErr := fmt.Errorf("did not get an error")
   321  	triggeredHandler := func(errer watcher.Errer) error {
   322  		foundErr = errer.Err()
   323  		return foundErr
   324  	}
   325  	legacy.SetEnsureErr(triggeredHandler)
   326  	s.actor.watcher.SetStopError(tomb.ErrStillAlive)
   327  	s.actor.watcher.Stop()
   328  	err := waitShort(c, s.worker)
   329  	c.Check(foundErr, gc.Equals, tomb.ErrStillAlive)
   330  	// ErrStillAlive is trapped by the Stop logic and gets turned into a
   331  	// 'nil' when stopping. However TestDefaultClosedHandler can assert
   332  	// that it would have triggered a panic.
   333  	c.Check(err, jc.ErrorIsNil)
   334  	s.actor.CheckActions(c, "setup", "teardown")
   335  	// Worker is stopped, don't fail TearDownTest
   336  	s.worker = nil
   337  }
   338  
   339  func (s *NotifyWorkerSuite) TestErrorsOnClosedChannel(c *gc.C) {
   340  	foundErr := fmt.Errorf("did not get an error")
   341  	triggeredHandler := func(errer watcher.Errer) error {
   342  		foundErr = errer.Err()
   343  		return foundErr
   344  	}
   345  	legacy.SetEnsureErr(triggeredHandler)
   346  	s.actor.watcher.Stop()
   347  	err := waitShort(c, s.worker)
   348  	// If the foundErr is nil, we would have panic-ed (see TestDefaultClosedHandler)
   349  	c.Check(foundErr, gc.IsNil)
   350  	c.Check(err, jc.ErrorIsNil)
   351  	s.actor.CheckActions(c, "setup", "teardown")
   352  }