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