github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/cleaner/cleaner_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package cleaner_test
     5  
     6  import (
     7  	"errors"
     8  	"time"
     9  
    10  	"github.com/juju/clock/testclock"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  	"gopkg.in/juju/worker.v1"
    14  	"gopkg.in/tomb.v2"
    15  
    16  	"github.com/juju/juju/core/watcher"
    17  	coretesting "github.com/juju/juju/testing"
    18  	"github.com/juju/juju/worker/cleaner"
    19  )
    20  
    21  type CleanerSuite struct {
    22  	coretesting.BaseSuite
    23  	mockState *cleanerMock
    24  	mockClock *testclock.Clock
    25  }
    26  
    27  var _ = gc.Suite(&CleanerSuite{})
    28  
    29  func (s *CleanerSuite) SetUpTest(c *gc.C) {
    30  	s.BaseSuite.SetUpTest(c)
    31  	s.mockState = &cleanerMock{
    32  		calls: make(chan string, 1),
    33  	}
    34  	s.mockState.watcher = s.newMockNotifyWatcher(nil)
    35  	s.mockClock = testclock.NewClock(time.Time{})
    36  }
    37  
    38  func (s *CleanerSuite) AssertReceived(c *gc.C, expect string) {
    39  	select {
    40  	case call := <-s.mockState.calls:
    41  		c.Assert(call, gc.Matches, expect)
    42  	case <-time.After(coretesting.LongWait):
    43  		c.Fatalf("Timed out waiting for %s", expect)
    44  	}
    45  }
    46  
    47  func (s *CleanerSuite) AssertEmpty(c *gc.C) {
    48  	select {
    49  	case call, ok := <-s.mockState.calls:
    50  		c.Fatalf("Unexpected %s (ok: %v)", call, ok)
    51  	case <-time.After(coretesting.ShortWait):
    52  	}
    53  }
    54  
    55  func (s *CleanerSuite) TestCleaner(c *gc.C) {
    56  	cln, err := cleaner.NewCleaner(s.mockState, s.mockClock)
    57  	c.Assert(err, jc.ErrorIsNil)
    58  	defer func() { c.Assert(worker.Stop(cln), jc.ErrorIsNil) }()
    59  
    60  	s.AssertReceived(c, "WatchCleanups")
    61  	s.AssertReceived(c, "Cleanup")
    62  	s.AssertEmpty(c)
    63  
    64  	s.mockState.watcher.Change()
    65  	s.AssertReceived(c, "Cleanup")
    66  	s.AssertEmpty(c)
    67  }
    68  
    69  func (s *CleanerSuite) TestCleanerPeriodic(c *gc.C) {
    70  	cln, err := cleaner.NewCleaner(s.mockState, s.mockClock)
    71  	c.Assert(err, jc.ErrorIsNil)
    72  	defer func() { c.Assert(worker.Stop(cln), jc.ErrorIsNil) }()
    73  
    74  	s.AssertReceived(c, "WatchCleanups")
    75  	s.AssertReceived(c, "Cleanup")
    76  	s.AssertEmpty(c)
    77  
    78  	// The cleaner will start a timer that waits for 30 seconds after
    79  	// each call to Cleanup, regardless of whether the previous call
    80  	// to Cleanup was triggered by the watcher or a timer.
    81  	for i := 0; i < 2; i++ {
    82  		s.mockClock.WaitAdvance(29*time.Second, coretesting.LongWait, 1)
    83  		s.AssertEmpty(c)
    84  		s.mockClock.WaitAdvance(1*time.Second, coretesting.LongWait, 1)
    85  		s.AssertReceived(c, "Cleanup")
    86  		s.AssertEmpty(c)
    87  	}
    88  }
    89  
    90  func (s *CleanerSuite) TestWatchCleanupsError(c *gc.C) {
    91  	s.mockState.err = []error{errors.New("hello")}
    92  	_, err := cleaner.NewCleaner(s.mockState, s.mockClock)
    93  	c.Assert(err, gc.ErrorMatches, "hello")
    94  
    95  	s.AssertReceived(c, "WatchCleanups")
    96  	s.AssertEmpty(c)
    97  }
    98  
    99  func (s *CleanerSuite) TestCleanupError(c *gc.C) {
   100  	s.mockState.err = []error{nil, errors.New("hello")}
   101  	cln, err := cleaner.NewCleaner(s.mockState, s.mockClock)
   102  	c.Assert(err, jc.ErrorIsNil)
   103  
   104  	s.AssertReceived(c, "WatchCleanups")
   105  	s.AssertReceived(c, "Cleanup")
   106  	err = worker.Stop(cln)
   107  	c.Assert(err, jc.ErrorIsNil)
   108  	log := c.GetTestLog()
   109  	c.Assert(log, jc.Contains, "ERROR juju.worker.cleaner cannot cleanup state: hello")
   110  }
   111  
   112  func (s *CleanerSuite) newMockNotifyWatcher(err error) *mockNotifyWatcher {
   113  	m := &mockNotifyWatcher{
   114  		changes: make(chan struct{}, 1),
   115  		err:     err,
   116  	}
   117  	m.tomb.Go(func() error {
   118  		<-m.tomb.Dying()
   119  		return m.err
   120  	})
   121  	s.AddCleanup(func(c *gc.C) {
   122  		err := worker.Stop(m)
   123  		c.Check(err, jc.ErrorIsNil)
   124  	})
   125  	m.Change()
   126  	return m
   127  }
   128  
   129  type mockNotifyWatcher struct {
   130  	watcher.NotifyWatcher
   131  
   132  	tomb    tomb.Tomb
   133  	err     error
   134  	changes chan struct{}
   135  }
   136  
   137  func (m *mockNotifyWatcher) Kill() {
   138  	m.tomb.Kill(nil)
   139  }
   140  
   141  func (m *mockNotifyWatcher) Wait() error {
   142  	return m.tomb.Wait()
   143  }
   144  
   145  func (m *mockNotifyWatcher) Changes() watcher.NotifyChannel {
   146  	return m.changes
   147  }
   148  
   149  func (m *mockNotifyWatcher) Change() {
   150  	m.changes <- struct{}{}
   151  }
   152  
   153  // cleanerMock is used to check the
   154  // calls of Cleanup() and WatchCleanups()
   155  type cleanerMock struct {
   156  	cleaner.StateCleaner
   157  	watcher *mockNotifyWatcher
   158  	calls   chan string
   159  	err     []error
   160  }
   161  
   162  func (m *cleanerMock) getError() (e error) {
   163  	if len(m.err) > 0 {
   164  		e = m.err[0]
   165  		m.err = m.err[1:]
   166  	}
   167  	return
   168  }
   169  
   170  func (m *cleanerMock) Cleanup() error {
   171  	m.calls <- "Cleanup"
   172  	return m.getError()
   173  }
   174  
   175  func (m *cleanerMock) WatchCleanups() (watcher.NotifyWatcher, error) {
   176  	m.calls <- "WatchCleanups"
   177  	return m.watcher, m.getError()
   178  }