github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/watcher/legacy/stringsworker_test.go (about)

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