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