github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/testing/watcher.go (about) 1 // Copyright 2012-2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package testing 5 6 import ( 7 "time" 8 9 "github.com/juju/collections/set" 10 jc "github.com/juju/testing/checkers" 11 gc "gopkg.in/check.v1" 12 tomb "gopkg.in/tomb.v2" 13 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/testing" 16 ) 17 18 type Stopper interface { 19 Stop() error 20 } 21 22 func AssertStop(c *gc.C, stopper Stopper) { 23 c.Assert(stopper.Stop(), gc.IsNil) 24 } 25 26 type KillWaiter interface { 27 Kill() 28 Wait() error 29 } 30 31 func AssertKillAndWait(c *gc.C, killWaiter KillWaiter) { 32 killWaiter.Kill() 33 c.Assert(killWaiter.Wait(), jc.ErrorIsNil) 34 } 35 36 // AssertCanStopWhenSending ensures even when there are changes 37 // pending to be delivered by the watcher it can still stop 38 // cleanly. This is necessary to check for deadlocks in case the 39 // watcher's inner loop is blocked trying to send and its tomb is 40 // already dying. 41 func AssertCanStopWhenSending(c *gc.C, stopper Stopper) { 42 // Leave some time for the event to be delivered and the watcher 43 // to block on sending it. 44 <-time.After(testing.ShortWait) 45 stopped := make(chan bool) 46 // Stop() blocks, so we need to call it in a separate goroutine. 47 go func() { 48 c.Check(stopper.Stop(), gc.IsNil) 49 stopped <- true 50 }() 51 select { 52 case <-time.After(testing.LongWait): 53 // NOTE: If this test fails here it means we have a deadlock 54 // in the client-side watcher implementation. 55 c.Fatalf("watcher did not stop as expected") 56 case <-stopped: 57 } 58 } 59 60 type NotifyWatcher interface { 61 Stop() error 62 Changes() <-chan struct{} 63 } 64 65 // NotifyWatcherC embeds a gocheck.C and adds methods to help verify 66 // the behaviour of any watcher that uses a <-chan struct{}. 67 type NotifyWatcherC struct { 68 *gc.C 69 State SyncStarter 70 Watcher NotifyWatcher 71 } 72 73 // SyncStarter is an interface that watcher checkers will use to ensure 74 // that changes to the watched object have been synchronized. This is 75 // primarily implemented by state.State. 76 type SyncStarter interface { 77 StartSync() 78 } 79 80 // NewNotifyWatcherC returns a NotifyWatcherC that checks for aggressive 81 // event coalescence. 82 func NewNotifyWatcherC(c *gc.C, st SyncStarter, w NotifyWatcher) NotifyWatcherC { 83 return NotifyWatcherC{ 84 C: c, 85 State: st, 86 Watcher: w, 87 } 88 } 89 90 func (c NotifyWatcherC) AssertNoChange() { 91 c.State.StartSync() 92 select { 93 case _, ok := <-c.Watcher.Changes(): 94 c.Fatalf("watcher sent unexpected change: (_, %v)", ok) 95 case <-time.After(testing.ShortWait): 96 } 97 } 98 99 func (c NotifyWatcherC) AssertOneChange() { 100 // Wait a very small amount of time, so that if there is already an event 101 // queued to be processed, we see it, before the StartSync flushes new 102 // events into the queue. 103 shortTimeout := time.After(1 * time.Millisecond) 104 longTimeout := time.After(testing.LongWait) 105 loop: 106 for { 107 select { 108 case _, ok := <-c.Watcher.Changes(): 109 c.C.Logf("got change") 110 c.Assert(ok, jc.IsTrue) 111 break loop 112 case <-shortTimeout: 113 c.C.Logf("StartSync()") 114 c.State.StartSync() 115 shortTimeout = nil 116 case <-longTimeout: 117 c.Fatalf("watcher did not send change") 118 break loop 119 } 120 } 121 c.AssertNoChange() 122 } 123 124 func (c NotifyWatcherC) AssertClosed() { 125 select { 126 case _, ok := <-c.Watcher.Changes(): 127 c.Assert(ok, jc.IsFalse) 128 default: 129 c.Fatalf("watcher not closed") 130 } 131 } 132 133 // StringsWatcherC embeds a gocheck.C and adds methods to help verify 134 // the behaviour of any watcher that uses a <-chan []string. 135 type StringsWatcherC struct { 136 *gc.C 137 State SyncStarter 138 Watcher StringsWatcher 139 } 140 141 // NewStringsWatcherC returns a StringsWatcherC that checks for aggressive 142 // event coalescence. 143 func NewStringsWatcherC(c *gc.C, st SyncStarter, w StringsWatcher) StringsWatcherC { 144 return StringsWatcherC{ 145 C: c, 146 State: st, 147 Watcher: w, 148 } 149 } 150 151 type StringsWatcher interface { 152 Stop() error 153 Changes() <-chan []string 154 } 155 156 func (c StringsWatcherC) AssertNoChange() { 157 c.State.StartSync() 158 select { 159 case actual, ok := <-c.Watcher.Changes(): 160 c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok) 161 case <-time.After(testing.ShortWait): 162 } 163 } 164 165 func (c StringsWatcherC) AssertChanges() { 166 c.State.StartSync() 167 select { 168 case <-c.Watcher.Changes(): 169 case <-time.After(testing.LongWait): 170 c.Fatalf("watcher did not send change") 171 } 172 } 173 174 func (c StringsWatcherC) AssertChange(expect ...string) { 175 // We should assert for either a single or multiple changes, 176 // based on the number of `expect` changes. 177 c.assertChange(len(expect) == 1, expect...) 178 } 179 180 func (c StringsWatcherC) AssertChangeInSingleEvent(expect ...string) { 181 c.assertChange(true, expect...) 182 } 183 184 // AssertChangeMaybeIncluding verifies that there is a change that may 185 // contain zero to all of the passed in strings, and no other changes. 186 func (c StringsWatcherC) AssertChangeMaybeIncluding(expect ...string) { 187 maxCount := len(expect) 188 actual := c.collectChanges(true, maxCount) 189 190 if maxCount == 0 { 191 c.Assert(actual, gc.HasLen, 0) 192 } else { 193 actualCount := len(actual) 194 c.Assert(actualCount <= maxCount, jc.IsTrue, gc.Commentf("expected at most %d, got %d", maxCount, actualCount)) 195 unexpected := set.NewStrings(actual...).Difference(set.NewStrings(expect...)) 196 c.Assert(unexpected.Values(), gc.HasLen, 0) 197 } 198 } 199 200 // assertChange asserts the given list of changes was reported by 201 // the watcher, but does not assume there are no following changes. 202 func (c StringsWatcherC) assertChange(single bool, expect ...string) { 203 actual := c.collectChanges(single, len(expect)) 204 if len(expect) == 0 { 205 c.Assert(actual, gc.HasLen, 0) 206 } else { 207 c.Assert(actual, jc.SameContents, expect) 208 } 209 } 210 211 // collectChanges gets up to the max number of changes within the 212 // testing.LongWait period. 213 func (c StringsWatcherC) collectChanges(single bool, max int) []string { 214 c.State.StartSync() 215 timeout := time.After(testing.LongWait) 216 var actual []string 217 gotOneChange := false 218 loop: 219 for { 220 select { 221 case changes, ok := <-c.Watcher.Changes(): 222 c.Assert(ok, jc.IsTrue) 223 gotOneChange = true 224 actual = append(actual, changes...) 225 if single || len(actual) >= max { 226 break loop 227 } 228 case <-timeout: 229 if !gotOneChange { 230 c.Fatalf("watcher did not send change") 231 } 232 // If we triggered a timeout, stop looking for more changes 233 break loop 234 } 235 } 236 return actual 237 } 238 239 func (c StringsWatcherC) AssertClosed() { 240 select { 241 case _, ok := <-c.Watcher.Changes(): 242 c.Assert(ok, jc.IsFalse) 243 default: 244 c.Fatalf("watcher not closed") 245 } 246 } 247 248 // RelationUnitsWatcherC embeds a gocheck.C and adds methods to help 249 // verify the behaviour of any watcher that uses a <-chan 250 // params.RelationUnitsChange. 251 type RelationUnitsWatcherC struct { 252 *gc.C 253 State SyncStarter 254 Watcher RelationUnitsWatcher 255 // settingsVersions keeps track of the settings version of each 256 // changed unit since the last received changes to ensure version 257 // always increases. 258 settingsVersions map[string]int64 259 } 260 261 // NewRelationUnitsWatcherC returns a RelationUnitsWatcherC that 262 // checks for aggressive event coalescence. 263 func NewRelationUnitsWatcherC(c *gc.C, st SyncStarter, w RelationUnitsWatcher) RelationUnitsWatcherC { 264 return RelationUnitsWatcherC{ 265 C: c, 266 State: st, 267 Watcher: w, 268 settingsVersions: make(map[string]int64), 269 } 270 } 271 272 type RelationUnitsWatcher interface { 273 Stop() error 274 Changes() <-chan params.RelationUnitsChange 275 } 276 277 func (c RelationUnitsWatcherC) AssertNoChange() { 278 c.State.StartSync() 279 select { 280 case actual, ok := <-c.Watcher.Changes(): 281 c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok) 282 case <-time.After(testing.ShortWait): 283 } 284 } 285 286 // AssertChange asserts the given changes was reported by the watcher, 287 // but does not assume there are no following changes. 288 func (c RelationUnitsWatcherC) AssertChange(changed []string, departed []string) { 289 // Get all items in changed in a map for easy lookup. 290 changedNames := make(map[string]bool) 291 for _, name := range changed { 292 changedNames[name] = true 293 } 294 c.State.StartSync() 295 timeout := time.After(testing.LongWait) 296 select { 297 case actual, ok := <-c.Watcher.Changes(): 298 c.Assert(ok, jc.IsTrue) 299 c.Assert(actual.Changed, gc.HasLen, len(changed)) 300 // Because the versions can change, we only need to make sure 301 // the keys match, not the contents (UnitSettings == txnRevno). 302 for k, settings := range actual.Changed { 303 _, ok := changedNames[k] 304 c.Assert(ok, jc.IsTrue) 305 oldVer, ok := c.settingsVersions[k] 306 if !ok { 307 // This is the first time we see this unit, so 308 // save the settings version for later. 309 c.settingsVersions[k] = settings.Version 310 } else { 311 // Already seen; make sure the version increased. 312 if settings.Version <= oldVer { 313 c.Fatalf("expected unit settings version > %d (got %d)", oldVer, settings.Version) 314 } 315 } 316 } 317 c.Assert(actual.Departed, jc.SameContents, departed) 318 case <-timeout: 319 c.Fatalf("watcher did not send change") 320 } 321 } 322 323 func (c RelationUnitsWatcherC) AssertClosed() { 324 select { 325 case _, ok := <-c.Watcher.Changes(): 326 c.Assert(ok, jc.IsFalse) 327 default: 328 c.Fatalf("watcher not closed") 329 } 330 } 331 332 // MockNotifyWatcher implements state.NotifyWatcher. 333 type MockNotifyWatcher struct { 334 tomb tomb.Tomb 335 ch <-chan struct{} 336 } 337 338 func NewMockNotifyWatcher(ch <-chan struct{}) *MockNotifyWatcher { 339 w := &MockNotifyWatcher{ch: ch} 340 w.tomb.Go(func() error { 341 <-w.tomb.Dying() 342 return tomb.ErrDying 343 }) 344 return w 345 } 346 347 func (w *MockNotifyWatcher) Changes() <-chan struct{} { 348 return w.ch 349 } 350 351 func (w *MockNotifyWatcher) Stop() error { 352 w.Kill() 353 return w.Wait() 354 } 355 356 func (w *MockNotifyWatcher) Kill() { 357 w.tomb.Kill(nil) 358 } 359 360 func (w *MockNotifyWatcher) Err() error { 361 return w.tomb.Err() 362 } 363 364 func (w *MockNotifyWatcher) Wait() error { 365 return w.tomb.Wait() 366 } 367 368 // MockStringsWatcher implements state.StringsWatcher. 369 type MockStringsWatcher struct { 370 tomb tomb.Tomb 371 ch <-chan []string 372 } 373 374 func NewMockStringsWatcher(ch <-chan []string) *MockStringsWatcher { 375 w := &MockStringsWatcher{ch: ch} 376 w.tomb.Go(func() error { 377 <-w.tomb.Dying() 378 return tomb.ErrDying 379 }) 380 return w 381 } 382 383 func (w *MockStringsWatcher) Changes() <-chan []string { 384 return w.ch 385 } 386 387 func (w *MockStringsWatcher) Stop() error { 388 w.Kill() 389 return w.Wait() 390 } 391 392 func (w *MockStringsWatcher) Kill() { 393 w.tomb.Kill(nil) 394 } 395 396 func (w *MockStringsWatcher) Err() error { 397 return w.tomb.Err() 398 } 399 400 func (w *MockStringsWatcher) Wait() error { 401 return w.tomb.Wait() 402 }