github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/stringsworker_test.go (about)

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