github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/watcher/notify_test.go (about)

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