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

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package worker
     5  
     6  import (
     7  	"time"
     8  
     9  	gc "gopkg.in/check.v1"
    10  
    11  	"github.com/juju/juju/testing"
    12  )
    13  
    14  type periodicWorkerSuite struct {
    15  	testing.BaseSuite
    16  }
    17  
    18  var (
    19  	_                   = gc.Suite(&periodicWorkerSuite{})
    20  	defaultPeriod       = time.Second
    21  	defaultFireOnceWait = defaultPeriod / 2
    22  )
    23  
    24  func (s *periodicWorkerSuite) TestWait(c *gc.C) {
    25  	funcHasRun := make(chan struct{})
    26  	doWork := func(_ <-chan struct{}) error {
    27  		funcHasRun <- struct{}{}
    28  		return testError
    29  	}
    30  
    31  	w := NewPeriodicWorker(doWork, defaultPeriod, NewTimer)
    32  	defer func() { c.Assert(Stop(w), gc.Equals, testError) }()
    33  	select {
    34  	case <-funcHasRun:
    35  	case <-time.After(testing.ShortWait):
    36  		c.Fatalf("The doWork function should have been called by now")
    37  	}
    38  	w.Kill()
    39  	c.Assert(w.Wait(), gc.Equals, testError)
    40  	select {
    41  	case <-funcHasRun:
    42  		c.Fatalf("After the kill we don't expect anymore calls to the function")
    43  	case <-time.After(defaultFireOnceWait):
    44  	}
    45  }
    46  
    47  // TestWaitNil starts a periodicWorker asserts that after
    48  // killing the worker Wait() returns nil after at least
    49  // one call of the doWork function
    50  func (s *periodicWorkerSuite) TestWaitNil(c *gc.C) {
    51  	funcHasRun := make(chan struct{})
    52  	doWork := func(_ <-chan struct{}) error {
    53  		funcHasRun <- struct{}{}
    54  		return nil
    55  	}
    56  
    57  	w := NewPeriodicWorker(doWork, defaultPeriod, NewTimer)
    58  	defer func() { c.Assert(Stop(w), gc.IsNil) }()
    59  	select {
    60  	case <-funcHasRun:
    61  	case <-time.After(defaultFireOnceWait):
    62  		c.Fatalf("The doWork function should have been called by now")
    63  	}
    64  	w.Kill()
    65  	c.Assert(w.Wait(), gc.Equals, nil)
    66  }
    67  
    68  // TestKill starts a periodic worker and Kills it
    69  // it expects the doWork function to be notified of this and the error from
    70  // doWork is returned by Wait()
    71  func (s *periodicWorkerSuite) TestKill(c *gc.C) {
    72  	tests := []struct {
    73  		ReturnValue   error
    74  		ExpectedValue error
    75  	}{
    76  		{nil, nil},
    77  		{testError, testError},
    78  		{ErrKilled, nil},
    79  	}
    80  
    81  	for i, test := range tests {
    82  		c.Logf("Running test %d\n", i)
    83  		runKillTest(c, test.ReturnValue, test.ExpectedValue)
    84  	}
    85  }
    86  
    87  func runKillTest(c *gc.C, returnValue, expected error) {
    88  	ready := make(chan struct{})
    89  	doWorkNotification := make(chan struct{})
    90  	doWork := func(stopCh <-chan struct{}) error {
    91  		close(ready)
    92  		<-stopCh
    93  		close(doWorkNotification)
    94  		return returnValue
    95  	}
    96  
    97  	w := NewPeriodicWorker(doWork, defaultPeriod, NewTimer)
    98  	defer func() { c.Assert(Stop(w), gc.Equals, expected) }()
    99  
   100  	select {
   101  	case <-ready:
   102  	case <-time.After(testing.LongWait):
   103  		c.Fatalf("The doWork call should be ready by now")
   104  	}
   105  	w.Kill()
   106  	select {
   107  	case <-doWorkNotification:
   108  	case <-time.After(testing.LongWait):
   109  		c.Fatalf("The doWork function should have been notified of the stop by now")
   110  	}
   111  	c.Assert(w.Wait(), gc.Equals, expected)
   112  
   113  	// test we can kill again without a panic and our death reason stays intact
   114  	w.Kill()
   115  }
   116  
   117  // TestCallUntilKilled checks that our function is called
   118  // at least 5 times, and that with a period of 500ms each call is made
   119  // in a reasonable time
   120  func (s *periodicWorkerSuite) TestCallUntilKilled(c *gc.C) {
   121  	funcHasRun := make(chan struct{}, 5)
   122  	doWork := func(_ <-chan struct{}) error {
   123  		funcHasRun <- struct{}{}
   124  		return nil
   125  	}
   126  
   127  	period := time.Millisecond * 500
   128  	unacceptableWait := time.Second * 10
   129  	w := NewPeriodicWorker(doWork, period, NewTimer)
   130  	defer func() { c.Assert(Stop(w), gc.IsNil) }()
   131  	for i := 0; i < 5; i++ {
   132  		select {
   133  		case <-funcHasRun:
   134  		case <-time.After(unacceptableWait):
   135  			c.Fatalf("The function should have been called again by now")
   136  		}
   137  	}
   138  	w.Kill()
   139  	c.Assert(w.Wait(), gc.Equals, nil)
   140  }