github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/pruner/worker_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package pruner_test 5 6 import ( 7 "sync" 8 "time" 9 10 "github.com/juju/clock/testclock" 11 "github.com/juju/errors" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 "gopkg.in/juju/worker.v1" 15 16 "github.com/juju/juju/core/watcher" 17 "github.com/juju/juju/environs/config" 18 coretesting "github.com/juju/juju/testing" 19 "github.com/juju/juju/worker/pruner" 20 "github.com/juju/juju/worker/statushistorypruner" 21 ) 22 23 type PrunerSuite struct { 24 coretesting.BaseSuite 25 } 26 27 var _ = gc.Suite(&PrunerSuite{}) 28 29 func (s *PrunerSuite) setupPruner(c *gc.C) (*fakeFacade, *testclock.Clock) { 30 facade := newFakeFacade() 31 attrs := coretesting.FakeConfig() 32 attrs["max-status-history-age"] = "1s" 33 attrs["max-status-history-size"] = "3M" 34 cfg, err := config.New(config.UseDefaults, attrs) 35 c.Assert(err, jc.ErrorIsNil) 36 facade.modelConfig = cfg 37 38 testClock := testclock.NewClock(time.Time{}) 39 conf := pruner.Config{ 40 Facade: facade, 41 PruneInterval: coretesting.ShortWait, 42 Clock: testClock, 43 } 44 45 // an example pruner 46 pruner, err := statushistorypruner.New(conf) 47 48 c.Check(err, jc.ErrorIsNil) 49 s.AddCleanup(func(*gc.C) { 50 c.Assert(worker.Stop(pruner), jc.ErrorIsNil) 51 }) 52 53 facade.changesWatcher.changes <- struct{}{} 54 select { 55 case <-facade.gotConfig: 56 case <-time.After(coretesting.LongWait): 57 c.Fatal("timed out waiting for model configr") 58 } 59 60 return facade, testClock 61 } 62 63 func (s *PrunerSuite) assertWorkerCallsPrune(c *gc.C, facade *fakeFacade, testClock *testclock.Clock, collectionSize int) { 64 // NewTimer/Reset will have been called with the PruneInterval. 65 testClock.WaitAdvance(coretesting.ShortWait-time.Nanosecond, coretesting.LongWait, 1) 66 select { 67 case <-facade.pruned: 68 c.Fatal("unexpected call to Prune") 69 case <-time.After(coretesting.ShortWait): 70 } 71 testClock.Advance(time.Nanosecond) 72 select { 73 case args := <-facade.pruned: 74 c.Assert(args.maxHistoryMB, gc.Equals, collectionSize) 75 case <-time.After(coretesting.LongWait): 76 c.Fatal("timed out waiting for call to Prune") 77 } 78 } 79 80 func (s *PrunerSuite) TestWorkerCallsPrune(c *gc.C) { 81 facade, clock := s.setupPruner(c) 82 s.assertWorkerCallsPrune(c, facade, clock, 3) 83 } 84 85 func (s *PrunerSuite) TestWorkerWontCallPruneBeforeFiringTimer(c *gc.C) { 86 facade, _ := s.setupPruner(c) 87 88 select { 89 case <-facade.pruned: 90 c.Fatal("called before firing timer.") 91 case <-time.After(coretesting.ShortWait): 92 } 93 } 94 95 func (s *PrunerSuite) TestModelConfigChange(c *gc.C) { 96 facade, clock := s.setupPruner(c) 97 s.assertWorkerCallsPrune(c, facade, clock, 3) 98 99 var err error 100 facade.modelConfig, err = facade.modelConfig.Apply(map[string]interface{}{"max-status-history-size": "4M"}) 101 c.Assert(err, jc.ErrorIsNil) 102 facade.changesWatcher.changes <- struct{}{} 103 104 s.assertWorkerCallsPrune(c, facade, clock, 4) 105 } 106 107 type fakeFacade struct { 108 pruned chan pruneParams 109 changesWatcher *mockNotifyWatcher 110 modelConfig *config.Config 111 gotConfig chan struct{} 112 } 113 114 type pruneParams struct { 115 maxAge time.Duration 116 maxHistoryMB int 117 } 118 119 func newFakeFacade() *fakeFacade { 120 return &fakeFacade{ 121 pruned: make(chan pruneParams, 1), 122 gotConfig: make(chan struct{}, 1), 123 changesWatcher: newMockNotifyWatcher(), 124 } 125 } 126 127 // Prune implements Facade 128 func (f *fakeFacade) Prune(maxAge time.Duration, maxHistoryMB int) error { 129 select { 130 case f.pruned <- pruneParams{maxAge, maxHistoryMB}: 131 case <-time.After(coretesting.LongWait): 132 return errors.New("timed out waiting for facade call Prune to run") 133 } 134 return nil 135 } 136 137 // WatchForModelConfigChanges implements Facade 138 func (f *fakeFacade) WatchForModelConfigChanges() (watcher.NotifyWatcher, error) { 139 return f.changesWatcher, nil 140 } 141 142 // ModelConfig implements Facade 143 func (f *fakeFacade) ModelConfig() (*config.Config, error) { 144 f.gotConfig <- struct{}{} 145 return f.modelConfig, nil 146 } 147 148 func newMockWatcher() *mockWatcher { 149 return &mockWatcher{ 150 stopped: make(chan struct{}), 151 } 152 } 153 154 type mockWatcher struct { 155 mu sync.Mutex 156 stopped chan struct{} 157 } 158 159 func (w *mockWatcher) Kill() { 160 w.mu.Lock() 161 defer w.mu.Unlock() 162 if !w.Stopped() { 163 close(w.stopped) 164 } 165 } 166 167 func (w *mockWatcher) Wait() error { 168 <-w.stopped 169 return nil 170 } 171 172 func (w *mockWatcher) Stopped() bool { 173 select { 174 case <-w.stopped: 175 return true 176 default: 177 return false 178 } 179 } 180 181 func newMockNotifyWatcher() *mockNotifyWatcher { 182 return &mockNotifyWatcher{ 183 mockWatcher: newMockWatcher(), 184 changes: make(chan struct{}, 1), 185 } 186 } 187 188 type mockNotifyWatcher struct { 189 *mockWatcher 190 changes chan struct{} 191 } 192 193 func (w *mockNotifyWatcher) Changes() watcher.NotifyChannel { 194 return w.changes 195 }