github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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 "gopkg.in/tomb.v2" 13 14 "github.com/juju/juju/core/watcher" 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 Watcher NotifyWatcher 70 } 71 72 // NewNotifyWatcherC returns a NotifyWatcherC that checks for aggressive 73 // event coalescence. 74 func NewNotifyWatcherC(c *gc.C, w NotifyWatcher) NotifyWatcherC { 75 return NotifyWatcherC{ 76 C: c, 77 Watcher: w, 78 } 79 } 80 81 func (c NotifyWatcherC) AssertNoChange() { 82 select { 83 case _, ok := <-c.Watcher.Changes(): 84 if ok { 85 c.Fatalf("watcher sent unexpected change") 86 } else { 87 c.Fatalf("watcher closed Changes channel") 88 } 89 case <-time.After(testing.ShortWait): 90 } 91 } 92 93 func (c NotifyWatcherC) AssertOneChange() { 94 longTimeout := time.After(testing.LongWait) 95 loop: 96 for { 97 select { 98 case _, ok := <-c.Watcher.Changes(): 99 c.C.Logf("got change") 100 c.Assert(ok, jc.IsTrue) 101 break loop 102 case <-longTimeout: 103 c.Fatalf("watcher did not send change") 104 break loop 105 } 106 } 107 c.AssertNoChange() 108 } 109 110 func (c NotifyWatcherC) AssertAtleastOneChange() { 111 longTimeout := time.After(testing.LongWait) 112 loop: 113 for { 114 select { 115 case _, ok := <-c.Watcher.Changes(): 116 c.C.Logf("got change") 117 c.Assert(ok, jc.IsTrue) 118 break loop 119 case <-longTimeout: 120 c.Fatalf("watcher did not send change") 121 break loop 122 } 123 } 124 } 125 126 // TODO(quiescence): reimplement quiescence and delete this utility 127 func (c NotifyWatcherC) AssertChanges(n int) { 128 longTimeout := time.After(testing.LongWait) 129 got := 0 130 loop: 131 for got < n { 132 select { 133 case _, ok := <-c.Watcher.Changes(): 134 c.C.Logf("got change") 135 c.Assert(ok, jc.IsTrue) 136 got++ 137 case <-longTimeout: 138 c.Fatalf("watcher did not %d send change(s)", n-got) 139 break loop 140 } 141 } 142 c.AssertNoChange() 143 } 144 145 func (c NotifyWatcherC) AssertClosed() { 146 select { 147 case _, ok := <-c.Watcher.Changes(): 148 c.Assert(ok, jc.IsFalse) 149 default: 150 c.Fatalf("watcher not closed") 151 } 152 } 153 154 // StringsWatcherC embeds a gocheck.C and adds methods to help verify 155 // the behaviour of any watcher that uses a <-chan []string. 156 type StringsWatcherC struct { 157 *gc.C 158 Watcher StringsWatcher 159 } 160 161 // NewStringsWatcherC returns a StringsWatcherC that checks for aggressive 162 // event coalescence. 163 func NewStringsWatcherC(c *gc.C, w StringsWatcher) StringsWatcherC { 164 return StringsWatcherC{ 165 C: c, 166 Watcher: w, 167 } 168 } 169 170 type StringsWatcher interface { 171 Stop() error 172 Changes() <-chan []string 173 } 174 175 func (c StringsWatcherC) AssertNoChange() { 176 select { 177 case actual, ok := <-c.Watcher.Changes(): 178 c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok) 179 case <-time.After(testing.ShortWait): 180 } 181 } 182 183 func (c StringsWatcherC) AssertChanges() { 184 select { 185 case <-c.Watcher.Changes(): 186 case <-time.After(testing.LongWait): 187 c.Fatalf("watcher did not send change") 188 } 189 } 190 191 func (c StringsWatcherC) AssertChange(expect ...string) { 192 // We should assert for either a single or multiple changes, 193 // based on the number of `expect` changes. 194 c.assertChange(len(expect) == 1, expect...) 195 } 196 197 func (c StringsWatcherC) AssertChangeInSingleEvent(expect ...string) { 198 c.assertChange(true, expect...) 199 } 200 201 // AssertChangeMaybeIncluding verifies that there is a change that may 202 // contain zero to all of the passed in strings, and no other changes. 203 func (c StringsWatcherC) AssertChangeMaybeIncluding(expect ...string) { 204 maxCount := len(expect) 205 actual := c.collectChanges(true, maxCount) 206 207 if maxCount == 0 { 208 c.Assert(actual, gc.HasLen, 0) 209 } else { 210 actualCount := len(actual) 211 c.Assert(actualCount <= maxCount, jc.IsTrue, gc.Commentf("expected at most %d, got %d", maxCount, actualCount)) 212 unexpected := set.NewStrings(actual...).Difference(set.NewStrings(expect...)) 213 c.Assert(unexpected.Values(), gc.HasLen, 0) 214 } 215 } 216 217 // assertChange asserts the given list of changes was reported by 218 // the watcher, but does not assume there are no following changes. 219 func (c StringsWatcherC) assertChange(single bool, expect ...string) { 220 actual := c.collectChanges(single, len(expect)) 221 if len(expect) == 0 { 222 c.Assert(actual, gc.HasLen, 0) 223 } else { 224 c.Assert(actual, jc.SameContents, expect) 225 } 226 } 227 228 // collectChanges gets up to the max number of changes within the 229 // testing.LongWait period. 230 func (c StringsWatcherC) collectChanges(single bool, max int) []string { 231 timeout := time.After(testing.LongWait) 232 var actual []string 233 gotOneChange := false 234 loop: 235 for { 236 select { 237 case changes, ok := <-c.Watcher.Changes(): 238 c.Assert(ok, jc.IsTrue) 239 gotOneChange = true 240 actual = append(actual, changes...) 241 if single || len(actual) >= max { 242 break loop 243 } 244 case <-timeout: 245 if !gotOneChange { 246 c.Fatalf("watcher did not send change") 247 } 248 // If we triggered a timeout, stop looking for more changes 249 break loop 250 } 251 } 252 return actual 253 } 254 255 func (c StringsWatcherC) AssertClosed() { 256 select { 257 case _, ok := <-c.Watcher.Changes(): 258 c.Assert(ok, jc.IsFalse) 259 default: 260 c.Fatalf("watcher not closed") 261 } 262 } 263 264 // RelationUnitsWatcherC embeds a gocheck.C and adds methods to help 265 // verify the behaviour of any watcher that uses a <-chan 266 // params.RelationUnitsChange. 267 type RelationUnitsWatcherC struct { 268 *gc.C 269 Watcher RelationUnitsWatcher 270 // settingsVersions keeps track of the settings version of each 271 // changed unit since the last received changes to ensure version 272 // always increases. 273 settingsVersions map[string]int64 274 appSettingsVersions map[string]int64 275 } 276 277 // NewRelationUnitsWatcherC returns a RelationUnitsWatcherC that 278 // checks for aggressive event coalescence. 279 func NewRelationUnitsWatcherC(c *gc.C, w RelationUnitsWatcher) RelationUnitsWatcherC { 280 return RelationUnitsWatcherC{ 281 C: c, 282 Watcher: w, 283 settingsVersions: make(map[string]int64), 284 appSettingsVersions: make(map[string]int64), 285 } 286 } 287 288 type RelationUnitsWatcher interface { 289 Stop() error 290 Changes() watcher.RelationUnitsChannel 291 } 292 293 func (c RelationUnitsWatcherC) AssertNoChange() { 294 select { 295 case actual, ok := <-c.Watcher.Changes(): 296 c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok) 297 case <-time.After(testing.ShortWait): 298 } 299 } 300 301 // AssertChange asserts the given changes was reported by the watcher, 302 // but does not assume there are no following changes. 303 func (c RelationUnitsWatcherC) AssertChange(changed []string, appChanged []string, departed []string) { 304 // Get all items in changed in a map for easy lookup. 305 changedNames := set.NewStrings(changed...) 306 appChangedNames := set.NewStrings(appChanged...) 307 timeout := time.After(testing.LongWait) 308 select { 309 case actual, ok := <-c.Watcher.Changes(): 310 c.Logf("Watcher.Changes() => %# v", actual) 311 c.Assert(ok, jc.IsTrue) 312 c.Check(actual.Changed, gc.HasLen, len(changed)) 313 c.Check(actual.AppChanged, gc.HasLen, len(appChanged)) 314 // Because the versions can change, we only need to make sure 315 // the keys match, not the contents (UnitSettings == txnRevno). 316 for k, settings := range actual.Changed { 317 c.Check(changedNames.Contains(k), jc.IsTrue) 318 oldVer, ok := c.settingsVersions[k] 319 if !ok { 320 // TODO(jam): 2019-10-22 shouldn't we update this *every* time we see it? 321 // This is the first time we see this unit, so 322 // save the settings version for later. 323 c.settingsVersions[k] = settings.Version 324 } else { 325 // Already seen; make sure the version increased. 326 c.Assert(settings.Version, jc.GreaterThan, oldVer, 327 gc.Commentf("expected unit settings to increase got %d had %d", 328 settings.Version, oldVer)) 329 } 330 } 331 for k, version := range actual.AppChanged { 332 c.Check(appChangedNames.Contains(k), jc.IsTrue) 333 oldVer, ok := c.appSettingsVersions[k] 334 if ok { 335 // Make sure if we've seen this setting before, it has been updated 336 c.Assert(version, jc.GreaterThan, oldVer, 337 gc.Commentf("expected app settings to increase got %d had %d", 338 version, oldVer)) 339 } 340 c.appSettingsVersions[k] = version 341 } 342 c.Check(actual.Departed, jc.SameContents, departed) 343 case <-timeout: 344 c.Fatalf("watcher did not send change") 345 } 346 } 347 348 func (c RelationUnitsWatcherC) AssertClosed() { 349 select { 350 case _, ok := <-c.Watcher.Changes(): 351 c.Assert(ok, jc.IsFalse) 352 default: 353 c.Fatalf("watcher not closed") 354 } 355 } 356 357 // SecretsTriggerWatcherC embeds a gocheck.C and adds methods to help 358 // verify the behaviour of any watcher that uses a 359 // <-chan []SecretTriggerChange 360 type SecretsTriggerWatcherC struct { 361 *gc.C 362 Watcher SecretsTriggerWatcher 363 } 364 365 // NewSecretsTriggerWatcherC returns a SecretsTriggerWatcherC that 366 // checks for aggressive event coalescence. 367 func NewSecretsTriggerWatcherC(c *gc.C, w SecretsTriggerWatcher) SecretsTriggerWatcherC { 368 return SecretsTriggerWatcherC{ 369 C: c, 370 Watcher: w, 371 } 372 } 373 374 type SecretsTriggerWatcher interface { 375 Stop() error 376 Changes() watcher.SecretTriggerChannel 377 } 378 379 func (c SecretsTriggerWatcherC) AssertNoChange() { 380 select { 381 case actual, ok := <-c.Watcher.Changes(): 382 c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok) 383 case <-time.After(testing.ShortWait): 384 } 385 } 386 387 // AssertChange asserts the given changes was reported by the watcher, 388 // but does not assume there are no following changes. 389 func (c SecretsTriggerWatcherC) AssertChange(expect ...watcher.SecretTriggerChange) { 390 var received []watcher.SecretTriggerChange 391 timeout := time.After(testing.LongWait) 392 for a := testing.LongAttempt.Start(); a.Next(); { 393 select { 394 case actual, ok := <-c.Watcher.Changes(): 395 c.Logf("Secrets Trigger Watcher.Changes() => %# v", actual) 396 c.Assert(ok, jc.IsTrue) 397 received = append(received, actual...) 398 if len(received) >= len(expect) { 399 mc := jc.NewMultiChecker() 400 mc.AddExpr(`_[_].NextTriggerTime`, jc.Almost, jc.ExpectedValue) 401 c.Assert(received, mc, expect) 402 return 403 } 404 case <-timeout: 405 c.Fatalf("watcher did not send change") 406 } 407 } 408 } 409 410 func (c SecretsTriggerWatcherC) AssertClosed() { 411 select { 412 case _, ok := <-c.Watcher.Changes(): 413 c.Assert(ok, jc.IsFalse) 414 default: 415 c.Fatalf("watcher not closed") 416 } 417 } 418 419 // SecretBackendRotateWatcherC embeds a gocheck.C and adds methods to help 420 // verify the behaviour of any watcher that uses a 421 // <-chan []SecretBackendRotateChange 422 type SecretBackendRotateWatcherC struct { 423 *gc.C 424 Watcher SecretBackendRotateWatcher 425 } 426 427 // NewSecretBackendRotateWatcherC returns a SecretBackendRotateWatcherC that 428 // checks for aggressive event coalescence. 429 func NewSecretBackendRotateWatcherC(c *gc.C, w SecretBackendRotateWatcher) SecretBackendRotateWatcherC { 430 return SecretBackendRotateWatcherC{ 431 C: c, 432 Watcher: w, 433 } 434 } 435 436 type SecretBackendRotateWatcher interface { 437 Stop() error 438 Changes() watcher.SecretBackendRotateChannel 439 } 440 441 func (c SecretBackendRotateWatcherC) AssertNoChange() { 442 select { 443 case actual, ok := <-c.Watcher.Changes(): 444 c.Fatalf("watcher sent unexpected change: (%v, %v)", actual, ok) 445 case <-time.After(testing.ShortWait): 446 } 447 } 448 449 // AssertChange asserts the given changes was reported by the watcher, 450 // but does not assume there are no following changes. 451 func (c SecretBackendRotateWatcherC) AssertChange(expect ...watcher.SecretBackendRotateChange) { 452 var received []watcher.SecretBackendRotateChange 453 timeout := time.After(testing.LongWait) 454 for a := testing.LongAttempt.Start(); a.Next(); { 455 select { 456 case actual, ok := <-c.Watcher.Changes(): 457 c.Logf("Secrets Trigger Watcher.Changes() => %# v", actual) 458 c.Assert(ok, jc.IsTrue) 459 received = append(received, actual...) 460 if len(received) >= len(expect) { 461 mc := jc.NewMultiChecker() 462 mc.AddExpr(`_[_].NextTriggerTime`, jc.Almost, jc.ExpectedValue) 463 c.Assert(received, mc, expect) 464 return 465 } 466 case <-timeout: 467 c.Fatalf("watcher did not send change") 468 } 469 } 470 } 471 472 func (c SecretBackendRotateWatcherC) AssertClosed() { 473 select { 474 case _, ok := <-c.Watcher.Changes(): 475 c.Assert(ok, jc.IsFalse) 476 default: 477 c.Fatalf("watcher not closed") 478 } 479 } 480 481 // MockNotifyWatcher implements state.NotifyWatcher. 482 type MockNotifyWatcher struct { 483 tomb tomb.Tomb 484 ch <-chan struct{} 485 } 486 487 func NewMockNotifyWatcher(ch <-chan struct{}) *MockNotifyWatcher { 488 w := &MockNotifyWatcher{ch: ch} 489 w.tomb.Go(func() error { 490 <-w.tomb.Dying() 491 return tomb.ErrDying 492 }) 493 return w 494 } 495 496 func (w *MockNotifyWatcher) Changes() <-chan struct{} { 497 return w.ch 498 } 499 500 func (w *MockNotifyWatcher) Stop() error { 501 w.Kill() 502 return w.Wait() 503 } 504 505 func (w *MockNotifyWatcher) Kill() { 506 w.tomb.Kill(nil) 507 } 508 509 func (w *MockNotifyWatcher) Err() error { 510 return w.tomb.Err() 511 } 512 513 func (w *MockNotifyWatcher) Wait() error { 514 return w.tomb.Wait() 515 } 516 517 // MockStringsWatcher implements state.StringsWatcher. 518 type MockStringsWatcher struct { 519 tomb tomb.Tomb 520 ch <-chan []string 521 } 522 523 func NewMockStringsWatcher(ch <-chan []string) *MockStringsWatcher { 524 w := &MockStringsWatcher{ch: ch} 525 w.tomb.Go(func() error { 526 <-w.tomb.Dying() 527 return tomb.ErrDying 528 }) 529 return w 530 } 531 532 func (w *MockStringsWatcher) Changes() <-chan []string { 533 return w.ch 534 } 535 536 func (w *MockStringsWatcher) Stop() error { 537 w.Kill() 538 return w.Wait() 539 } 540 541 func (w *MockStringsWatcher) Kill() { 542 w.tomb.Kill(nil) 543 } 544 545 func (w *MockStringsWatcher) Err() error { 546 return w.tomb.Err() 547 } 548 549 func (w *MockStringsWatcher) Wait() error { 550 return w.tomb.Wait() 551 }