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 }