github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/testing/suite.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package testing 5 6 import ( 7 "sync" 8 "time" 9 10 "github.com/juju/clock/testclock" 11 jujutesting "github.com/juju/testing" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 "gopkg.in/juju/names.v2" 15 retry "gopkg.in/retry.v1" 16 17 "github.com/juju/juju/cloud" 18 "github.com/juju/juju/environs/config" 19 "github.com/juju/juju/state" 20 statewatcher "github.com/juju/juju/state/watcher" 21 "github.com/juju/juju/testing" 22 "github.com/juju/juju/testing/factory" 23 ) 24 25 var _ = gc.Suite(&StateSuite{}) 26 27 // StateSuite provides setup and teardown for tests that require a 28 // state.State. 29 type StateSuite struct { 30 jujutesting.MgoSuite 31 testing.BaseSuite 32 NewPolicy state.NewPolicyFunc 33 Controller *state.Controller 34 StatePool *state.StatePool 35 State *state.State 36 Model *state.Model 37 Owner names.UserTag 38 Factory *factory.Factory 39 InitialConfig *config.Config 40 InitialTime time.Time 41 ControllerConfig map[string]interface{} 42 ControllerInheritedConfig map[string]interface{} 43 RegionConfig cloud.RegionConfig 44 Clock *testclock.Clock 45 txnSyncNotify chan struct{} 46 modelWatcherIdle chan string 47 modelWatcherMutex *sync.Mutex 48 } 49 50 func (s *StateSuite) SetUpSuite(c *gc.C) { 51 s.MgoSuite.SetUpSuite(c) 52 s.BaseSuite.SetUpSuite(c) 53 } 54 55 func (s *StateSuite) TearDownSuite(c *gc.C) { 56 s.BaseSuite.TearDownSuite(c) 57 s.MgoSuite.TearDownSuite(c) 58 } 59 60 func (s *StateSuite) SetUpTest(c *gc.C) { 61 s.MgoSuite.SetUpTest(c) 62 s.BaseSuite.SetUpTest(c) 63 64 s.txnSyncNotify = make(chan struct{}) 65 s.modelWatcherIdle = nil 66 s.modelWatcherMutex = &sync.Mutex{} 67 s.PatchValue(&statewatcher.TxnPollNotifyFunc, s.txnNotifyFunc) 68 s.PatchValue(&statewatcher.HubWatcherIdleFunc, s.hubWatcherIdleFunc) 69 70 s.Owner = names.NewLocalUserTag("test-admin") 71 initialTime := s.InitialTime 72 if initialTime.IsZero() { 73 initialTime = testing.NonZeroTime() 74 } 75 s.Clock = testclock.NewClock(initialTime) 76 // Patch the polling policy of the primary txn watcher for the 77 // state pool. Since we are using a testing clock the StartSync 78 // method on the state object advances the clock one second. 79 // Make the txn poller use a standard one second poll interval. 80 s.PatchValue( 81 &statewatcher.PollStrategy, 82 retry.Exponential{ 83 Initial: time.Second, 84 Factor: 1.0, 85 }) 86 87 s.Controller = InitializeWithArgs(c, InitializeArgs{ 88 Owner: s.Owner, 89 InitialConfig: s.InitialConfig, 90 ControllerConfig: s.ControllerConfig, 91 ControllerInheritedConfig: s.ControllerInheritedConfig, 92 RegionConfig: s.RegionConfig, 93 NewPolicy: s.NewPolicy, 94 Clock: s.Clock, 95 }) 96 s.AddCleanup(func(*gc.C) { 97 s.Controller.Close() 98 close(s.txnSyncNotify) 99 }) 100 s.StatePool = s.Controller.StatePool() 101 s.State = s.StatePool.SystemState() 102 model, err := s.State.Model() 103 c.Assert(err, jc.ErrorIsNil) 104 s.Model = model 105 106 s.Factory = factory.NewFactory(s.State, s.StatePool) 107 } 108 109 func (s *StateSuite) TearDownTest(c *gc.C) { 110 s.BaseSuite.TearDownTest(c) 111 s.MgoSuite.TearDownTest(c) 112 } 113 114 func (s *StateSuite) txnNotifyFunc() { 115 select { 116 case s.txnSyncNotify <- struct{}{}: 117 // Try to send something down the channel. 118 default: 119 // However don't get stressed if noone is listening. 120 } 121 } 122 123 func (s *StateSuite) hubWatcherIdleFunc(modelUUID string) { 124 s.modelWatcherMutex.Lock() 125 idleChan := s.modelWatcherIdle 126 s.modelWatcherMutex.Unlock() 127 if idleChan == nil { 128 return 129 } 130 idleChan <- modelUUID 131 } 132 133 // WaitForNextSync repeatedly advances the testing clock 134 // with short waits between until the txn poller doesn't find 135 // any more changes. 136 func (s *StateSuite) WaitForNextSync(c *gc.C) { 137 done := make(chan struct{}) 138 go func() { 139 <-s.txnSyncNotify 140 close(done) 141 }() 142 timeout := time.After(jujutesting.LongWait) 143 for { 144 s.Clock.Advance(time.Second) 145 loop := time.After(10 * time.Millisecond) 146 select { 147 case <-done: 148 return 149 case <-loop: 150 case <-timeout: 151 c.Fatal("no sync event sent, is the watcher dead?") 152 } 153 } 154 } 155 156 // WaitForModelWatchersIdle firstly waits for the txn poller to process 157 // all pending changes, then waits for the hub watcher on the state object 158 // to have finished processing all those events. 159 func (s *StateSuite) WaitForModelWatchersIdle(c *gc.C, modelUUID string) { 160 c.Logf("waiting for model %s to be idle", modelUUID) 161 s.modelWatcherMutex.Lock() 162 idleChan := make(chan string) 163 s.modelWatcherIdle = idleChan 164 s.modelWatcherMutex.Unlock() 165 s.WaitForNextSync(c) 166 167 defer func() { 168 s.modelWatcherMutex.Lock() 169 s.modelWatcherIdle = nil 170 s.modelWatcherMutex.Unlock() 171 // Clear out any pending events. 172 for { 173 select { 174 case <-idleChan: 175 default: 176 return 177 } 178 } 179 }() 180 181 timeout := time.After(jujutesting.LongWait) 182 for { 183 s.Clock.Advance(10 * time.Millisecond) 184 loop := time.After(10 * time.Millisecond) 185 select { 186 case <-loop: 187 case uuid := <-idleChan: 188 if uuid == modelUUID { 189 return 190 } else { 191 c.Logf("model %s is idle", uuid) 192 } 193 case <-timeout: 194 c.Fatal("no sync event sent, is the watcher dead?") 195 } 196 } 197 }