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