github.com/onsi/gomega@v1.32.0/internal/async_assertion_test.go (about) 1 package internal_test 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "reflect" 8 "runtime" 9 "strings" 10 "time" 11 12 . "github.com/onsi/ginkgo/v2" 13 . "github.com/onsi/gomega" 14 ) 15 16 type quickMatcher struct { 17 matchFunc func(actual any) (bool, error) 18 oracleFunc func(actual any) bool 19 } 20 21 func (q quickMatcher) Match(actual any) (bool, error) { 22 return q.matchFunc(actual) 23 } 24 25 func (q quickMatcher) FailureMessage(actual any) (message string) { 26 return fmt.Sprintf("QM failure message: %v", actual) 27 } 28 29 func (q quickMatcher) NegatedFailureMessage(actual any) (message string) { 30 return fmt.Sprintf("QM negated failure message: %v", actual) 31 } 32 33 func (q quickMatcher) MatchMayChangeInTheFuture(actual interface{}) bool { 34 if q.oracleFunc == nil { 35 return true 36 } 37 return q.oracleFunc(actual) 38 } 39 40 func QuickMatcher(matchFunc func(actual any) (bool, error)) OmegaMatcher { 41 return quickMatcher{matchFunc, nil} 42 } 43 44 func QuickMatcherWithOracle(matchFunc func(actual any) (bool, error), oracleFunc func(actual any) bool) OmegaMatcher { 45 return quickMatcher{matchFunc, oracleFunc} 46 } 47 48 type FakeGinkgoSpecContext struct { 49 Attached func() string 50 Cancelled bool 51 } 52 53 func (f *FakeGinkgoSpecContext) AttachProgressReporter(v func() string) func() { 54 f.Attached = v 55 return func() { f.Cancelled = true } 56 } 57 58 var _ = Describe("Asynchronous Assertions", func() { 59 var ig *InstrumentedGomega 60 BeforeEach(func() { 61 ig = NewInstrumentedGomega() 62 }) 63 64 Describe("Basic Eventually support", func() { 65 Context("the positive case", func() { 66 It("polls the function and matcher until a match occurs", func() { 67 counter := 0 68 ig.G.Eventually(func() string { 69 counter++ 70 if counter > 5 { 71 return MATCH 72 } 73 return NO_MATCH 74 }).Should(SpecMatch()) 75 Ω(counter).Should(Equal(6)) 76 Ω(ig.FailureMessage).Should(BeZero()) 77 }) 78 79 It("continues polling even if the matcher errors", func() { 80 counter := 0 81 ig.G.Eventually(func() string { 82 counter++ 83 if counter > 5 { 84 return MATCH 85 } 86 return ERR_MATCH 87 }).Should(SpecMatch()) 88 Ω(counter).Should(Equal(6)) 89 Ω(ig.FailureMessage).Should(BeZero()) 90 }) 91 92 It("times out eventually if the assertion doesn't match in time", func() { 93 counter := 0 94 ig.G.Eventually(func() string { 95 counter++ 96 if counter > 100 { 97 return MATCH 98 } 99 return NO_MATCH 100 }).WithTimeout(200 * time.Millisecond).WithPolling(20 * time.Millisecond).Should(SpecMatch()) 101 Ω(counter).Should(BeNumerically(">", 2)) 102 Ω(counter).Should(BeNumerically("<", 20)) 103 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out after")) 104 Ω(ig.FailureMessage).Should(ContainSubstring("positive: no match")) 105 Ω(ig.FailureSkip).Should(Equal([]int{3})) 106 }) 107 108 It("maps Within() correctly to timeout and polling intervals", func() { 109 counter := 0 110 ig.G.Eventually(func() bool { 111 counter++ 112 return false 113 }).WithTimeout(0).WithPolling(20 * time.Millisecond).Within(200 * time.Millisecond).Should(BeTrue()) 114 Ω(counter).Should(BeNumerically(">", 2)) 115 Ω(counter).Should(BeNumerically("<", 20)) 116 117 counter = 0 118 ig.G.Eventually(func() bool { 119 counter++ 120 return false 121 }).WithTimeout(0).WithPolling(0). // first zero intervals, then set them 122 Within(200 * time.Millisecond).ProbeEvery(20 * time.Millisecond). 123 Should(BeTrue()) 124 Ω(counter).Should(BeNumerically(">", 2)) 125 Ω(counter).Should(BeNumerically("<", 20)) 126 }) 127 }) 128 129 Context("the negative case", func() { 130 It("polls the function and matcher until a match does not occur", func() { 131 counter := 0 132 ig.G.Eventually(func() string { 133 counter++ 134 if counter > 5 { 135 return NO_MATCH 136 } 137 return MATCH 138 }).ShouldNot(SpecMatch()) 139 Ω(counter).Should(Equal(6)) 140 Ω(ig.FailureMessage).Should(BeZero()) 141 }) 142 143 It("continues polling when the matcher errors - an error does not count as a successful non-match", func() { 144 counter := 0 145 ig.G.Eventually(func() string { 146 counter++ 147 if counter > 5 { 148 return NO_MATCH 149 } 150 return ERR_MATCH 151 }).ShouldNot(SpecMatch()) 152 Ω(counter).Should(Equal(6)) 153 Ω(ig.FailureMessage).Should(BeZero()) 154 }) 155 156 It("times out eventually if the assertion doesn't match in time", func() { 157 counter := 0 158 ig.G.Eventually(func() string { 159 counter++ 160 if counter > 100 { 161 return NO_MATCH 162 } 163 return MATCH 164 }).WithTimeout(200 * time.Millisecond).WithPolling(20 * time.Millisecond).ShouldNot(SpecMatch()) 165 Ω(counter).Should(BeNumerically(">", 2)) 166 Ω(counter).Should(BeNumerically("<", 20)) 167 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out after")) 168 Ω(ig.FailureMessage).Should(ContainSubstring("negative: match")) 169 Ω(ig.FailureSkip).Should(Equal([]int{3})) 170 }) 171 }) 172 173 Context("when a failure occurs", func() { 174 It("registers the appropriate helper functions", func() { 175 ig.G.Eventually(NO_MATCH).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(SpecMatch()) 176 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out after")) 177 Ω(ig.FailureMessage).Should(ContainSubstring("positive: no match")) 178 Ω(ig.FailureSkip).Should(Equal([]int{3})) 179 Ω(ig.RegisteredHelpers).Should(ContainElement("(*AsyncAssertion).Should")) 180 Ω(ig.RegisteredHelpers).Should(ContainElement("(*AsyncAssertion).match")) 181 }) 182 183 It("renders the matcher's error if an error occurred", func() { 184 ig.G.Eventually(ERR_MATCH).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(SpecMatch()) 185 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out after")) 186 Ω(ig.FailureMessage).Should(ContainSubstring("The matcher passed to Eventually returned the following error:")) 187 Ω(ig.FailureMessage).Should(ContainSubstring("spec matcher error")) 188 }) 189 190 It("renders the optional description", func() { 191 ig.G.Eventually(NO_MATCH).WithTimeout(50*time.Millisecond).WithPolling(10*time.Millisecond).Should(SpecMatch(), "boop") 192 Ω(ig.FailureMessage).Should(ContainSubstring("boop")) 193 }) 194 195 It("formats and renders the optional description when there are multiple arguments", func() { 196 ig.G.Eventually(NO_MATCH).WithTimeout(50*time.Millisecond).WithPolling(10*time.Millisecond).Should(SpecMatch(), "boop %d", 17) 197 Ω(ig.FailureMessage).Should(ContainSubstring("boop 17")) 198 }) 199 200 It("calls the optional description if it is a function", func() { 201 ig.G.Eventually(NO_MATCH).WithTimeout(50*time.Millisecond).WithPolling(10*time.Millisecond).Should(SpecMatch(), func() string { return "boop" }) 202 Ω(ig.FailureMessage).Should(ContainSubstring("boop")) 203 }) 204 }) 205 206 Context("with a passed-in context", func() { 207 Context("when the passed-in context is cancelled", func() { 208 It("stops and returns a failure", func() { 209 ctx, cancel := context.WithCancel(context.Background()) 210 counter := 0 211 ig.G.Eventually(func() string { 212 counter++ 213 if counter == 2 { 214 cancel() 215 } else if counter == 10 { 216 return MATCH 217 } 218 return NO_MATCH 219 }, time.Hour, ctx).Should(SpecMatch()) 220 Ω(ig.FailureMessage).Should(ContainSubstring("Context was cancelled after")) 221 Ω(ig.FailureMessage).Should(ContainSubstring("positive: no match")) 222 }) 223 224 It("can also be configured via WithContext()", func() { 225 ctx, cancel := context.WithCancel(context.Background()) 226 counter := 0 227 ig.G.Eventually(func() string { 228 counter++ 229 if counter == 2 { 230 cancel() 231 } else if counter == 10 { 232 return MATCH 233 } 234 return NO_MATCH 235 }, time.Hour).WithContext(ctx).Should(SpecMatch()) 236 Ω(ig.FailureMessage).Should(ContainSubstring("Context was cancelled after")) 237 Ω(ig.FailureMessage).Should(ContainSubstring("positive: no match")) 238 }) 239 240 It("can also be configured with the context up front", func() { 241 ctx, cancel := context.WithCancel(context.Background()) 242 counter := 0 243 ig.G.Eventually(ctx, func() string { 244 counter++ 245 if counter == 2 { 246 cancel() 247 } else if counter == 10 { 248 return MATCH 249 } 250 return NO_MATCH 251 }, time.Hour).Should(SpecMatch()) 252 Ω(ig.FailureMessage).Should(ContainSubstring("Context was cancelled after")) 253 Ω(ig.FailureMessage).Should(ContainSubstring("positive: no match")) 254 }) 255 256 It("treats a leading context as an actual, even if valid durations are passed in", func() { 257 ctx, cancel := context.WithCancel(context.Background()) 258 defer cancel() 259 Eventually(ctx).Should(Equal(ctx)) 260 Eventually(ctx, 0.1).Should(Equal(ctx)) 261 }) 262 263 It("counts as a failure for Consistently", func() { 264 ctx, cancel := context.WithCancel(context.Background()) 265 counter := 0 266 ig.G.Consistently(func() string { 267 counter++ 268 if counter == 2 { 269 cancel() 270 } else if counter == 10 { 271 return NO_MATCH 272 } 273 return MATCH 274 }, time.Hour).WithContext(ctx).Should(SpecMatch()) 275 Ω(ig.FailureMessage).Should(ContainSubstring("Context was cancelled after")) 276 Ω(ig.FailureMessage).Should(ContainSubstring("There is no failure as the matcher passed to Consistently has not yet failed")) 277 }) 278 279 It("includes the cancel cause if provided", func() { 280 ctx, cancel := context.WithCancelCause(context.Background()) 281 counter := 0 282 ig.G.Eventually(func() string { 283 counter++ 284 if counter == 2 { 285 cancel(fmt.Errorf("kaboom")) 286 } else if counter == 10 { 287 return MATCH 288 } 289 return NO_MATCH 290 }, time.Hour, ctx).Should(SpecMatch()) 291 Ω(ig.FailureMessage).Should(ContainSubstring("Context was cancelled (cause: kaboom) after")) 292 Ω(ig.FailureMessage).Should(ContainSubstring("positive: no match")) 293 }) 294 }) 295 296 Context("when the passed-in context is a Ginkgo SpecContext that can take a progress reporter attachment", func() { 297 It("attaches a progress reporter context that allows it to report on demand", func() { 298 fakeSpecContext := &FakeGinkgoSpecContext{} 299 var message string 300 ctx := context.WithValue(context.Background(), "GINKGO_SPEC_CONTEXT", fakeSpecContext) 301 ig.G.Eventually(func() string { 302 if fakeSpecContext.Attached != nil { 303 message = fakeSpecContext.Attached() 304 } 305 return NO_MATCH 306 }).WithTimeout(time.Millisecond * 20).WithContext(ctx).Should(Equal(MATCH)) 307 308 Ω(message).Should(Equal("Expected\n <string>: no match\nto equal\n <string>: match")) 309 Ω(fakeSpecContext.Cancelled).Should(BeTrue()) 310 }) 311 312 Context("when used with consistently", func() { 313 It("returns a useful message that does not invoke the matcher's failure handlers", func() { 314 fakeSpecContext := &FakeGinkgoSpecContext{} 315 var message string 316 ctx := context.WithValue(context.Background(), "GINKGO_SPEC_CONTEXT", fakeSpecContext) 317 ig.G.Consistently(func() error { 318 if fakeSpecContext.Attached != nil { 319 message = fakeSpecContext.Attached() 320 } 321 return nil 322 }).WithTimeout(time.Millisecond * 20).WithContext(ctx).ShouldNot(HaveOccurred()) 323 324 Ω(message).Should(Equal("There is no failure as the matcher passed to Consistently has not yet failed")) 325 Ω(fakeSpecContext.Cancelled).Should(BeTrue()) 326 }) 327 }) 328 }) 329 330 Describe("the interaction between the context and the timeout", func() { 331 It("only relies on context cancellation when no explicit timeout is specified", func() { 332 ig.G.SetDefaultEventuallyTimeout(time.Millisecond * 10) 333 ig.G.SetDefaultEventuallyPollingInterval(time.Millisecond * 40) 334 t := time.Now() 335 ctx, cancel := context.WithCancel(context.Background()) 336 iterations := 0 337 ig.G.Eventually(func() string { 338 iterations += 1 339 if time.Since(t) > time.Millisecond*200 { 340 cancel() 341 } 342 return "A" 343 }).WithContext(ctx).Should(Equal("B")) 344 Ω(time.Since(t)).Should(BeNumerically("~", time.Millisecond*200, time.Millisecond*100)) 345 Ω(iterations).Should(BeNumerically("~", 200/40, 2)) 346 Ω(ig.FailureMessage).Should(ContainSubstring("Context was cancelled after")) 347 }) 348 349 It("uses the explicit timeout when it is provided", func() { 350 t := time.Now() 351 ctx, cancel := context.WithCancel(context.Background()) 352 iterations := 0 353 ig.G.Eventually(func() string { 354 iterations += 1 355 if time.Since(t) > time.Millisecond*200 { 356 cancel() 357 } 358 return "A" 359 }).WithContext(ctx).WithTimeout(time.Millisecond * 80).ProbeEvery(time.Millisecond * 40).Should(Equal("B")) 360 Ω(time.Since(t)).Should(BeNumerically("~", time.Millisecond*80, time.Millisecond*40)) 361 Ω(iterations).Should(BeNumerically("~", 80/40, 2)) 362 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out after")) 363 }) 364 }) 365 }) 366 }) 367 368 Describe("Basic Consistently support", func() { 369 Context("the positive case", func() { 370 It("polls the function and matcher ensuring a match occurs consistently", func() { 371 counter := 0 372 ig.G.Consistently(func() string { 373 counter++ 374 return MATCH 375 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(SpecMatch()) 376 Ω(counter).Should(BeNumerically(">", 1)) 377 Ω(counter).Should(BeNumerically("<", 7)) 378 Ω(ig.FailureMessage).Should(BeZero()) 379 }) 380 381 It("fails if the matcher ever errors", func() { 382 counter := 0 383 ig.G.Consistently(func() string { 384 counter++ 385 if counter == 3 { 386 return ERR_MATCH 387 } 388 return MATCH 389 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(SpecMatch()) 390 Ω(counter).Should(Equal(3)) 391 Ω(ig.FailureMessage).Should(ContainSubstring("Failed after")) 392 Ω(ig.FailureMessage).Should(ContainSubstring("The matcher passed to Consistently returned the following error:")) 393 Ω(ig.FailureMessage).Should(ContainSubstring("spec matcher error")) 394 }) 395 396 It("fails if the matcher doesn't match at any point", func() { 397 counter := 0 398 ig.G.Consistently(func() string { 399 counter++ 400 if counter == 3 { 401 return NO_MATCH 402 } 403 return MATCH 404 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(SpecMatch()) 405 Ω(counter).Should(Equal(3)) 406 Ω(ig.FailureMessage).Should(ContainSubstring("Failed after")) 407 Ω(ig.FailureMessage).Should(ContainSubstring("positive: no match")) 408 }) 409 }) 410 411 Context("the negative case", func() { 412 It("polls the function and matcher ensuring a match never occurs", func() { 413 counter := 0 414 ig.G.Consistently(func() string { 415 counter++ 416 return NO_MATCH 417 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).ShouldNot(SpecMatch()) 418 Ω(counter).Should(BeNumerically(">", 1)) 419 Ω(counter).Should(BeNumerically("<", 7)) 420 Ω(ig.FailureMessage).Should(BeZero()) 421 }) 422 423 It("fails if the matcher ever errors", func() { 424 counter := 0 425 ig.G.Consistently(func() string { 426 counter++ 427 if counter == 3 { 428 return ERR_MATCH 429 } 430 return NO_MATCH 431 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).ShouldNot(SpecMatch()) 432 Ω(counter).Should(Equal(3)) 433 Ω(ig.FailureMessage).Should(ContainSubstring("Failed after")) 434 Ω(ig.FailureMessage).Should(ContainSubstring("spec matcher error")) 435 }) 436 437 It("fails if the matcher matches at any point", func() { 438 counter := 0 439 ig.G.Consistently(func() string { 440 counter++ 441 if counter == 3 { 442 return MATCH 443 } 444 return NO_MATCH 445 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).ShouldNot(SpecMatch()) 446 Ω(counter).Should(Equal(3)) 447 Ω(ig.FailureMessage).Should(ContainSubstring("Failed after")) 448 Ω(ig.FailureMessage).Should(ContainSubstring("negative: match")) 449 }) 450 }) 451 452 Context("when a failure occurs", func() { 453 It("registers the appropriate helper functions", func() { 454 ig.G.Consistently(NO_MATCH).Should(SpecMatch()) 455 Ω(ig.FailureMessage).Should(ContainSubstring("Failed after")) 456 Ω(ig.FailureMessage).Should(ContainSubstring("positive: no match")) 457 Ω(ig.FailureSkip).Should(Equal([]int{3})) 458 Ω(ig.RegisteredHelpers).Should(ContainElement("(*AsyncAssertion).Should")) 459 Ω(ig.RegisteredHelpers).Should(ContainElement("(*AsyncAssertion).match")) 460 }) 461 462 It("renders the matcher's error if an error occurred", func() { 463 ig.G.Consistently(ERR_MATCH).Should(SpecMatch()) 464 Ω(ig.FailureMessage).Should(ContainSubstring("Failed after")) 465 Ω(ig.FailureMessage).Should(ContainSubstring("The matcher passed to Consistently returned the following error:")) 466 Ω(ig.FailureMessage).Should(ContainSubstring("spec matcher error")) 467 }) 468 469 It("renders the optional description", func() { 470 ig.G.Consistently(NO_MATCH).Should(SpecMatch(), "boop") 471 Ω(ig.FailureMessage).Should(ContainSubstring("boop")) 472 }) 473 474 It("formats and renders the optional description when there are multiple arguments", func() { 475 ig.G.Consistently(NO_MATCH).Should(SpecMatch(), "boop %d", 17) 476 Ω(ig.FailureMessage).Should(ContainSubstring("boop 17")) 477 }) 478 479 It("calls the optional description if it is a function", func() { 480 ig.G.Consistently(NO_MATCH).Should(SpecMatch(), func() string { return "boop" }) 481 Ω(ig.FailureMessage).Should(ContainSubstring("boop")) 482 }) 483 }) 484 485 Context("with a passed-in context", func() { 486 Context("when the passed-in context is cancelled", func() { 487 It("counts as a failure for Consistently", func() { 488 ctx, cancel := context.WithCancel(context.Background()) 489 counter := 0 490 ig.G.Consistently(func() string { 491 counter++ 492 if counter == 2 { 493 cancel() 494 } else if counter == 10 { 495 return NO_MATCH 496 } 497 return MATCH 498 }, time.Hour).WithContext(ctx).Should(SpecMatch()) 499 Ω(ig.FailureMessage).Should(ContainSubstring("Context was cancelled after")) 500 Ω(ig.FailureMessage).Should(ContainSubstring("There is no failure as the matcher passed to Consistently has not yet failed")) 501 }) 502 }) 503 504 Describe("the interaction between the context and the timeout", func() { 505 It("only always uses the default interval even if not explicit duration is provided", func() { 506 ig.G.SetDefaultConsistentlyDuration(time.Millisecond * 200) 507 ig.G.SetDefaultConsistentlyPollingInterval(time.Millisecond * 40) 508 t := time.Now() 509 ctx, cancel := context.WithCancel(context.Background()) 510 defer cancel() 511 iterations := 0 512 ig.G.Consistently(func() string { 513 iterations += 1 514 return "A" 515 }).WithContext(ctx).Should(Equal("A")) 516 Ω(time.Since(t)).Should(BeNumerically("~", time.Millisecond*200, time.Millisecond*100)) 517 Ω(iterations).Should(BeNumerically("~", 200/40, 2)) 518 Ω(ig.FailureMessage).Should(BeZero()) 519 }) 520 }) 521 }) 522 }) 523 524 Describe("the passed-in actual", func() { 525 type Foo struct{ Bar string } 526 527 Context("when passed a value", func() { 528 It("(eventually) continuously checks on the value until a match occurs", func() { 529 c := make(chan bool) 530 go func() { 531 time.Sleep(100 * time.Millisecond) 532 close(c) 533 }() 534 ig.G.Eventually(c).WithTimeout(1 * time.Second).WithPolling(10 * time.Millisecond).Should(BeClosed()) 535 Ω(ig.FailureMessage).Should(BeZero()) 536 }) 537 538 It("(consistently) continuously checks on the value ensuring a match always occurs", func() { 539 c := make(chan bool) 540 close(c) 541 ig.G.Consistently(c).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(BeClosed()) 542 Ω(ig.FailureMessage).Should(BeZero()) 543 }) 544 }) 545 546 Context("when passed a function that takes no arguments and returns one value", func() { 547 It("(eventually) polls the function until the returned value satisfies the matcher", func() { 548 counter := 0 549 ig.G.Eventually(func() int { 550 counter += 1 551 return counter 552 }).WithTimeout(1 * time.Second).WithPolling(10 * time.Millisecond).Should(BeNumerically(">", 5)) 553 Ω(ig.FailureMessage).Should(BeZero()) 554 }) 555 556 It("(consistently) polls the function ensuring the returned value satisfies the matcher", func() { 557 counter := 0 558 ig.G.Consistently(func() int { 559 counter += 1 560 return counter 561 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(BeNumerically("<", 20)) 562 Ω(counter).Should(BeNumerically(">", 2)) 563 Ω(ig.FailureMessage).Should(BeZero()) 564 }) 565 566 It("works when the function returns nil", func() { 567 counter := 0 568 ig.G.Eventually(func() error { 569 counter += 1 570 if counter > 5 { 571 return nil 572 } 573 return errors.New("oops") 574 }).WithTimeout(1 * time.Second).WithPolling(10 * time.Millisecond).Should(BeNil()) 575 Ω(ig.FailureMessage).Should(BeZero()) 576 }) 577 }) 578 579 Context("when passed a function that takes no arguments and returns multiple values", func() { 580 Context("with Eventually", func() { 581 It("polls the function until the first returned value satisfies the matcher _and_ all additional values are zero", func() { 582 counter, s, f, err := 0, "hi", Foo{Bar: "hi"}, errors.New("hi") 583 ig.G.Eventually(func() (int, string, Foo, error) { 584 switch counter += 1; counter { 585 case 2: 586 s = "" 587 case 3: 588 f = Foo{} 589 case 4: 590 err = nil 591 } 592 return counter, s, f, err 593 }).WithTimeout(1 * time.Second).WithPolling(10 * time.Millisecond).Should(BeNumerically("<", 100)) 594 Ω(ig.FailureMessage).Should(BeZero()) 595 Ω(counter).Should(Equal(4)) 596 }) 597 598 It("reports on the non-zero value if it times out", func() { 599 ig.G.Eventually(func() (int, string, Foo, error) { 600 return 1, "", Foo{Bar: "hi"}, nil 601 }).WithTimeout(30 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(BeNumerically("<", 100)) 602 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually had an unexpected non-nil/non-zero return value at index 2:")) 603 Ω(ig.FailureMessage).Should(ContainSubstring(`<internal_test.Foo>: {Bar: "hi"}`)) 604 }) 605 606 It("has a meaningful message if all the return values are zero except the final return value, and it is an error", func() { 607 ig.G.Eventually(func() (int, string, Foo, error) { 608 return 1, "", Foo{}, errors.New("welp!") 609 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(BeNumerically("<", 100)) 610 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually returned the following error:")) 611 Ω(ig.FailureMessage).Should(ContainSubstring("welp!")) 612 }) 613 614 Context("when making a ShouldNot assertion", func() { 615 It("doesn't succeed until the matcher is (not) satisfied with the first returned value _and_ all additional values are zero", func() { 616 counter, s, f, err := 0, "hi", Foo{Bar: "hi"}, errors.New("hi") 617 ig.G.Eventually(func() (int, string, Foo, error) { 618 switch counter += 1; counter { 619 case 2: 620 s = "" 621 case 3: 622 f = Foo{} 623 case 4: 624 err = nil 625 } 626 return counter, s, f, err 627 }).WithTimeout(1 * time.Second).WithPolling(10 * time.Millisecond).ShouldNot(BeNumerically("<", 0)) 628 Ω(ig.FailureMessage).Should(BeZero()) 629 Ω(counter).Should(Equal(4)) 630 }) 631 }) 632 }) 633 634 Context("with Consistently", func() { 635 It("polls the function and succeeds if all the values are zero and the matcher is consistently satisfied", func() { 636 var err error 637 counter, s, f := 0, "", Foo{} 638 ig.G.Consistently(func() (int, string, Foo, error) { 639 counter += 1 640 return counter, s, f, err 641 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(BeNumerically("<", 100)) 642 Ω(ig.FailureMessage).Should(BeZero()) 643 Ω(counter).Should(BeNumerically(">", 2)) 644 }) 645 646 It("polls the function and fails any of the values are non-zero", func() { 647 var err error 648 counter, s, f := 0, "", Foo{} 649 ig.G.Consistently(func() (int, string, Foo, error) { 650 counter += 1 651 if counter == 3 { 652 f = Foo{Bar: "welp"} 653 } 654 return counter, s, f, err 655 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(BeNumerically("<", 100)) 656 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Consistently had an unexpected non-nil/non-zero return value at index 2:")) 657 Ω(ig.FailureMessage).Should(ContainSubstring(`<internal_test.Foo>: {Bar: "welp"}`)) 658 Ω(counter).Should(Equal(3)) 659 }) 660 661 Context("when making a ShouldNot assertion", func() { 662 It("succeeds if all additional values are zero", func() { 663 var err error 664 counter, s, f := 0, "", Foo{} 665 ig.G.Consistently(func() (int, string, Foo, error) { 666 counter += 1 667 return counter, s, f, err 668 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).ShouldNot(BeNumerically(">", 100)) 669 Ω(ig.FailureMessage).Should(BeZero()) 670 Ω(counter).Should(BeNumerically(">", 2)) 671 }) 672 673 It("fails if any additional values are ever non-zero", func() { 674 var err error 675 counter, s, f := 0, "", Foo{} 676 ig.G.Consistently(func() (int, string, Foo, error) { 677 counter += 1 678 if counter == 3 { 679 s = "welp" 680 } 681 return counter, s, f, err 682 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).ShouldNot(BeNumerically(">", 100)) 683 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Consistently had an unexpected non-nil/non-zero return value at index 1:")) 684 Ω(ig.FailureMessage).Should(ContainSubstring(`<string>: welp`)) 685 Ω(counter).Should(Equal(3)) 686 }) 687 }) 688 }) 689 }) 690 691 Context("when passed a function that takes a Gomega argument and returns values", func() { 692 Context("with Eventually", func() { 693 It("passes in a Gomega and passes if the matcher matches, all extra values are zero, and there are no failed assertions", func() { 694 counter, s, f, err := 0, "hi", Foo{Bar: "hi"}, errors.New("hi") 695 ig.G.Eventually(func(g Gomega) (int, string, Foo, error) { 696 switch counter += 1; counter { 697 case 2: 698 s = "" 699 case 3: 700 f = Foo{} 701 case 4: 702 err = nil 703 } 704 if counter == 5 { 705 g.Expect(true).To(BeTrue()) 706 } else { 707 g.Expect(false).To(BeTrue()) 708 panic("boom") //never see since the expectation stops execution 709 } 710 return counter, s, f, err 711 }).WithTimeout(1 * time.Second).WithPolling(10 * time.Millisecond).Should(BeNumerically("<", 100)) 712 Ω(ig.FailureMessage).Should(BeZero()) 713 Ω(counter).Should(Equal(5)) 714 }) 715 716 It("times out if assertions in the function never succeed and reports on the error", func() { 717 _, file, line, _ := runtime.Caller(0) 718 ig.G.Eventually(func(g Gomega) int { 719 g.Expect(false).To(BeTrue()) 720 return 10 721 }).WithTimeout(30 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(Equal(10)) 722 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually failed at %s:%d with:", file, line+2)) 723 Ω(ig.FailureMessage).Should(ContainSubstring("Expected\n <bool>: false\nto be true")) 724 }) 725 726 It("forwards panics", func() { 727 Ω(func() { 728 ig.G.Eventually(func(g Gomega) int { 729 g.Expect(true).To(BeTrue()) 730 panic("boom") 731 }).WithTimeout(30 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(Equal(10)) 732 }).Should(PanicWith("boom")) 733 Ω(ig.FailureMessage).Should(BeEmpty()) 734 }) 735 736 It("correctly handles the case (in concert with Ginkgo) when an assertion fails in a goroutine", func() { 737 count := 0 738 ig.G.Eventually(func(g Gomega) { 739 c := make(chan interface{}) 740 go func() { 741 defer GinkgoRecover() 742 defer close(c) 743 count += 1 744 g.Expect(count).To(Equal(3)) //panics! 745 }() 746 <-c 747 }).WithTimeout(30 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(Succeed()) 748 Ω(count).Should(Equal(3)) 749 }) 750 751 Context("when making a ShouldNot assertion", func() { 752 It("doesn't succeed until all extra values are zero, there are no failed assertions, and the matcher is (not) satisfied", func() { 753 counter, s, f, err := 0, "hi", Foo{Bar: "hi"}, errors.New("hi") 754 ig.G.Eventually(func(g Gomega) (int, string, Foo, error) { 755 switch counter += 1; counter { 756 case 2: 757 s = "" 758 case 3: 759 f = Foo{} 760 case 4: 761 err = nil 762 } 763 if counter == 5 { 764 g.Expect(true).To(BeTrue()) 765 } else { 766 g.Expect(false).To(BeTrue()) 767 panic("boom") //never see since the expectation stops execution 768 } 769 return counter, s, f, err 770 }).WithTimeout(1 * time.Second).WithPolling(10 * time.Millisecond).ShouldNot(BeNumerically("<", 0)) 771 Ω(ig.FailureMessage).Should(BeZero()) 772 Ω(counter).Should(Equal(5)) 773 }) 774 }) 775 776 It("fails if an assertion is never satisfied", func() { 777 _, file, line, _ := runtime.Caller(0) 778 ig.G.Eventually(func(g Gomega) int { 779 g.Expect(false).To(BeTrue()) 780 return 9 781 }).WithTimeout(30 * time.Millisecond).WithPolling(10 * time.Millisecond).ShouldNot(Equal(10)) 782 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually failed at %s:%d with:", file, line+2)) 783 Ω(ig.FailureMessage).Should(ContainSubstring("Expected\n <bool>: false\nto be true")) 784 }) 785 786 It("shows the state of the last match if there was a non-failing funciton at some point", func() { 787 counter := 0 788 _, file, line, _ := runtime.Caller(0) 789 ig.G.Eventually(func(g Gomega) int { 790 counter += 1 791 g.Expect(counter).To(BeNumerically("<", 3)) 792 return counter 793 }).WithTimeout(100 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(Equal(10)) 794 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually failed at %s:%d with:\nExpected\n <int>: ", file, line+3)) 795 Ω(ig.FailureMessage).Should(ContainSubstring("to be <\n <int>: 3")) 796 Ω(ig.FailureMessage).Should(ContainSubstring("At one point, however, the function did return successfully.\nYet, Eventually failed because the matcher was not satisfied:\nExpected\n <int>: 2\nto equal\n <int>: 10")) 797 }) 798 }) 799 800 Context("with Consistently", func() { 801 It("passes in a Gomega and passes if the matcher matches, all extra values are zero, and there are no failed assertions", func() { 802 var err error 803 counter, s, f := 0, "", Foo{} 804 ig.G.Consistently(func(g Gomega) (int, string, Foo, error) { 805 counter += 1 806 g.Expect(true).To(BeTrue()) 807 return counter, s, f, err 808 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(BeNumerically("<", 100)) 809 Ω(ig.FailureMessage).Should(BeZero()) 810 Ω(counter).Should(BeNumerically(">", 2)) 811 }) 812 813 It("fails if the passed-in gomega ever hits a failure", func() { 814 var err error 815 counter, s, f := 0, "", Foo{} 816 _, file, line, _ := runtime.Caller(0) 817 ig.G.Consistently(func(g Gomega) (int, string, Foo, error) { 818 counter += 1 819 g.Expect(true).To(BeTrue()) 820 if counter == 3 { 821 g.Expect(false).To(BeTrue()) 822 panic("boom") //never see this 823 } 824 return counter, s, f, err 825 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(BeNumerically("<", 100)) 826 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Consistently failed at %s:%d with:", file, line+5)) 827 Ω(ig.FailureMessage).Should(ContainSubstring("Expected\n <bool>: false\nto be true")) 828 Ω(counter).Should(Equal(3)) 829 }) 830 831 It("forwards panics", func() { 832 Ω(func() { 833 ig.G.Consistently(func(g Gomega) int { 834 g.Expect(true).To(BeTrue()) 835 panic("boom") 836 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(Equal(10)) 837 }).Should(PanicWith("boom")) 838 Ω(ig.FailureMessage).Should(BeEmpty()) 839 }) 840 841 Context("when making a ShouldNot assertion", func() { 842 It("succeeds if any interior assertions always pass", func() { 843 ig.G.Consistently(func(g Gomega) int { 844 g.Expect(true).To(BeTrue()) 845 return 9 846 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).ShouldNot(Equal(10)) 847 Ω(ig.FailureMessage).Should(BeEmpty()) 848 }) 849 850 It("fails if any interior assertions ever fail", func() { 851 counter := 0 852 _, file, line, _ := runtime.Caller(0) 853 ig.G.Consistently(func(g Gomega) int { 854 g.Expect(true).To(BeTrue()) 855 counter += 1 856 if counter == 3 { 857 g.Expect(false).To(BeTrue()) 858 panic("boom") //never see this 859 } 860 return 9 861 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).ShouldNot(Equal(10)) 862 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Consistently failed at %s:%d with:", file, line+5)) 863 Ω(ig.FailureMessage).Should(ContainSubstring("Expected\n <bool>: false\nto be true")) 864 }) 865 }) 866 }) 867 }) 868 869 Context("when passed a function that takes a Gomega argument and returns nothing", func() { 870 Context("with Eventually", func() { 871 It("returns the first failed assertion as an error and so should Succeed() if the callback ever runs without issue", func() { 872 counter := 0 873 ig.G.Eventually(func(g Gomega) { 874 counter += 1 875 if counter < 5 { 876 g.Expect(false).To(BeTrue()) 877 g.Expect("bloop").To(Equal("blarp")) 878 } 879 }).WithTimeout(1 * time.Second).WithPolling(10 * time.Millisecond).Should(Succeed()) 880 Ω(counter).Should(Equal(5)) 881 Ω(ig.FailureMessage).Should(BeZero()) 882 }) 883 884 It("returns the first failed assertion as an error and so should timeout if the callback always fails", func() { 885 counter := 0 886 ig.G.Eventually(func(g Gomega) { 887 counter += 1 888 if counter < 5000 { 889 g.Expect(false).To(BeTrue()) 890 g.Expect("bloop").To(Equal("blarp")) 891 } 892 }).WithTimeout(100 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(Succeed()) 893 Ω(counter).Should(BeNumerically(">", 1)) 894 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually failed at")) 895 Ω(ig.FailureMessage).Should(ContainSubstring("<bool>: false")) 896 Ω(ig.FailureMessage).Should(ContainSubstring("to be true")) 897 Ω(ig.FailureMessage).ShouldNot(ContainSubstring("bloop")) 898 }) 899 900 It("returns the first failed assertion as an error and should satisy ShouldNot(Succeed) eventually", func() { 901 counter := 0 902 ig.G.Eventually(func(g Gomega) { 903 counter += 1 904 if counter > 5 { 905 g.Expect(false).To(BeTrue()) 906 g.Expect("bloop").To(Equal("blarp")) 907 } 908 }).WithTimeout(100 * time.Millisecond).WithPolling(10 * time.Millisecond).ShouldNot(Succeed()) 909 Ω(counter).Should(Equal(6)) 910 Ω(ig.FailureMessage).Should(BeZero()) 911 }) 912 913 It("should fail to ShouldNot(Succeed) eventually if an error never occurs", func() { 914 ig.G.Eventually(func(g Gomega) { 915 g.Expect(true).To(BeTrue()) 916 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).ShouldNot(Succeed()) 917 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out after")) 918 Ω(ig.FailureMessage).Should(ContainSubstring("Expected failure, but got no error.")) 919 }) 920 }) 921 922 Context("with Consistently", func() { 923 It("returns the first failed assertion as an error and so should Succeed() if the callback always runs without issue", func() { 924 counter := 0 925 ig.G.Consistently(func(g Gomega) { 926 counter += 1 927 g.Expect(true).To(BeTrue()) 928 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(Succeed()) 929 Ω(counter).Should(BeNumerically(">", 2)) 930 Ω(ig.FailureMessage).Should(BeZero()) 931 }) 932 933 It("returns the first failed assertion as an error and so should fail if the callback ever fails", func() { 934 counter := 0 935 ig.G.Consistently(func(g Gomega) { 936 counter += 1 937 g.Expect(true).To(BeTrue()) 938 if counter == 3 { 939 g.Expect(false).To(BeTrue()) 940 g.Expect("bloop").To(Equal("blarp")) 941 } 942 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).Should(Succeed()) 943 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Consistently failed at")) 944 Ω(ig.FailureMessage).Should(ContainSubstring("<bool>: false")) 945 Ω(ig.FailureMessage).Should(ContainSubstring("to be true")) 946 Ω(ig.FailureMessage).ShouldNot(ContainSubstring("bloop")) 947 Ω(counter).Should(Equal(3)) 948 }) 949 950 It("returns the first failed assertion as an error and should satisy ShouldNot(Succeed) consistently if an error always occur", func() { 951 counter := 0 952 ig.G.Consistently(func(g Gomega) { 953 counter += 1 954 g.Expect(true).To(BeFalse()) 955 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).ShouldNot(Succeed()) 956 Ω(counter).Should(BeNumerically(">", 2)) 957 Ω(ig.FailureMessage).Should(BeZero()) 958 }) 959 960 It("should fail to satisfy ShouldNot(Succeed) consistently if an error ever does not occur", func() { 961 counter := 0 962 ig.G.Consistently(func(g Gomega) { 963 counter += 1 964 if counter == 3 { 965 g.Expect(true).To(BeTrue()) 966 } else { 967 g.Expect(false).To(BeTrue()) 968 } 969 }).WithTimeout(50 * time.Millisecond).WithPolling(10 * time.Millisecond).ShouldNot(Succeed()) 970 Ω(ig.FailureMessage).Should(ContainSubstring("Failed after")) 971 Ω(ig.FailureMessage).Should(ContainSubstring("Expected failure, but got no error.")) 972 Ω(counter).Should(Equal(3)) 973 }) 974 }) 975 }) 976 977 Context("when passed a function that takes a context", func() { 978 It("forwards its own configured context", func() { 979 ctx := context.WithValue(context.Background(), "key", "value") 980 Eventually(func(ctx context.Context) string { 981 return ctx.Value("key").(string) 982 }).WithContext(ctx).Should(Equal("value")) 983 }) 984 985 It("forwards its own configured context _and_ a Gomega if requested", func() { 986 ctx := context.WithValue(context.Background(), "key", "value") 987 Eventually(func(g Gomega, ctx context.Context) { 988 g.Expect(ctx.Value("key").(string)).To(Equal("schmalue")) 989 }).WithContext(ctx).Should(MatchError(ContainSubstring("Expected\n <string>: value\nto equal\n <string>: schmalue"))) 990 }) 991 992 Context("when the assertion does not have an attached context", func() { 993 It("errors", func() { 994 ig.G.Eventually(func(ctx context.Context) string { 995 return ctx.Value("key").(string) 996 }).Should(Equal("value")) 997 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually requested a context.Context, but no context has been provided. Please pass one in using Eventually().WithContext().")) 998 Ω(ig.FailureSkip).Should(Equal([]int{2})) 999 }) 1000 }) 1001 }) 1002 1003 Context("when passed a function that takes additional arguments", func() { 1004 Context("with just arguments", func() { 1005 It("forwards those arguments along", func() { 1006 Eventually(func(a int, b string) string { 1007 return fmt.Sprintf("%d - %s", a, b) 1008 }).WithArguments(10, "four").Should(Equal("10 - four")) 1009 1010 Eventually(func(a int, b string, c ...int) string { 1011 return fmt.Sprintf("%d - %s (%d%d%d)", a, b, c[0], c[1], c[2]) 1012 }).WithArguments(10, "four", 5, 1, 0).Should(Equal("10 - four (510)")) 1013 }) 1014 }) 1015 1016 Context("with a Gomega arugment as well", func() { 1017 It("can also forward arguments alongside a Gomega", func() { 1018 Eventually(func(g Gomega, a int, b int) { 1019 g.Expect(a).To(Equal(b)) 1020 }).WithArguments(10, 3).ShouldNot(Succeed()) 1021 Eventually(func(g Gomega, a int, b int) { 1022 g.Expect(a).To(Equal(b)) 1023 }).WithArguments(3, 3).Should(Succeed()) 1024 }) 1025 }) 1026 1027 Context("with a context arugment as well", func() { 1028 It("can also forward arguments alongside a context", func() { 1029 ctx := context.WithValue(context.Background(), "key", "value") 1030 Eventually(func(ctx context.Context, animal string) string { 1031 return ctx.Value("key").(string) + " " + animal 1032 }).WithArguments("pony").WithContext(ctx).Should(Equal("value pony")) 1033 }) 1034 }) 1035 1036 Context("with Gomega and context arugments", func() { 1037 It("forwards arguments alongside both", func() { 1038 ctx := context.WithValue(context.Background(), "key", "I have") 1039 f := func(g Gomega, ctx context.Context, count int, zoo ...string) { 1040 sentence := fmt.Sprintf("%s %d animals: %s", ctx.Value("key"), count, strings.Join(zoo, ", ")) 1041 g.Expect(sentence).To(Equal("I have 3 animals: dog, cat, pony")) 1042 } 1043 1044 Eventually(f).WithArguments(3, "dog", "cat", "pony").WithContext(ctx).Should(Succeed()) 1045 Eventually(f).WithArguments(2, "dog", "cat").WithContext(ctx).Should(MatchError(ContainSubstring("Expected\n <string>: I have 2 animals: dog, cat\nto equal\n <string>: I have 3 animals: dog, cat, pony"))) 1046 }) 1047 }) 1048 1049 Context("with a context that is in the argument list", func() { 1050 It("does not forward the configured context", func() { 1051 ctxA := context.WithValue(context.Background(), "key", "A") 1052 ctxB := context.WithValue(context.Background(), "key", "B") 1053 1054 Eventually(func(ctx context.Context, a string) string { 1055 return ctx.Value("key").(string) + " " + a 1056 }).WithContext(ctxA).WithArguments(ctxB, "C").Should(Equal("B C")) 1057 }) 1058 }) 1059 1060 Context("and an incorrect number of arguments is provided", func() { 1061 It("errors", func() { 1062 ig.G.Eventually(func(a int) string { 1063 return "" 1064 }).Should(Equal("foo")) 1065 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually has signature func(int) string takes 1 arguments but 0 have been provided. Please use Eventually().WithArguments() to pass the corect set of arguments.")) 1066 1067 ig.G.Eventually(func(a int, b int) string { 1068 return "" 1069 }).WithArguments(1).Should(Equal("foo")) 1070 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually has signature func(int, int) string takes 2 arguments but 1 has been provided. Please use Eventually().WithArguments() to pass the corect set of arguments.")) 1071 1072 ig.G.Eventually(func(a int, b int) string { 1073 return "" 1074 }).WithArguments(1, 2, 3).Should(Equal("foo")) 1075 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually has signature func(int, int) string takes 2 arguments but 3 have been provided. Please use Eventually().WithArguments() to pass the corect set of arguments.")) 1076 1077 ig.G.Eventually(func(g Gomega, a int, b int) string { 1078 return "" 1079 }).WithArguments(1, 2, 3).Should(Equal("foo")) 1080 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually has signature func(types.Gomega, int, int) string takes 3 arguments but 4 have been provided. Please use Eventually().WithArguments() to pass the corect set of arguments.")) 1081 1082 ig.G.Eventually(func(a int, b int, c ...int) string { 1083 return "" 1084 }).WithArguments(1).Should(Equal("foo")) 1085 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually has signature func(int, int, ...int) string takes 3 arguments but 1 has been provided. Please use Eventually().WithArguments() to pass the corect set of arguments.")) 1086 1087 }) 1088 }) 1089 }) 1090 1091 Describe("when passed an invalid function", func() { 1092 It("errors with a failure", func() { 1093 ig.G.Eventually(func() {}).Should(Equal("foo")) 1094 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually had an invalid signature of func()")) 1095 Ω(ig.FailureSkip).Should(Equal([]int{2})) 1096 1097 ig.G.Consistently(func(ctx context.Context) {}).Should(Equal("foo")) 1098 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Consistently had an invalid signature of func(context.Context)")) 1099 Ω(ig.FailureSkip).Should(Equal([]int{2})) 1100 1101 ig.G.Eventually(func(ctx context.Context, g Gomega) {}).Should(Equal("foo")) 1102 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually had an invalid signature of func(context.Context, types.Gomega)")) 1103 Ω(ig.FailureSkip).Should(Equal([]int{2})) 1104 1105 ig = NewInstrumentedGomega() 1106 ig.G.Eventually(func(foo string) {}).Should(Equal("foo")) 1107 Ω(ig.FailureMessage).Should(ContainSubstring("The function passed to Eventually had an invalid signature of func(string)")) 1108 Ω(ig.FailureSkip).Should(Equal([]int{2})) 1109 }) 1110 }) 1111 }) 1112 1113 Describe("Stopping Early", func() { 1114 Describe("when using OracleMatchers", func() { 1115 It("stops and gives up with an appropriate failure message if the OracleMatcher says things can't change", func() { 1116 c := make(chan bool) 1117 close(c) 1118 1119 t := time.Now() 1120 ig.G.Eventually(c).WithTimeout(100*time.Millisecond).WithPolling(10*time.Millisecond).Should(Receive(), "Receive is an OracleMatcher that gives up if the channel is closed") 1121 Ω(time.Since(t)).Should(BeNumerically("<", 90*time.Millisecond)) 1122 Ω(ig.FailureMessage).Should(ContainSubstring("No future change is possible.")) 1123 Ω(ig.FailureMessage).Should(ContainSubstring("The channel is closed.")) 1124 }) 1125 1126 It("never gives up if actual is a function", func() { 1127 c := make(chan bool) 1128 close(c) 1129 1130 t := time.Now() 1131 ig.G.Eventually(func() chan bool { return c }).WithTimeout(100*time.Millisecond).WithPolling(10*time.Millisecond).Should(Receive(), "Receive is an OracleMatcher that gives up if the channel is closed") 1132 Ω(time.Since(t)).Should(BeNumerically(">=", 90*time.Millisecond)) 1133 Ω(ig.FailureMessage).ShouldNot(ContainSubstring("No future change is possible.")) 1134 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out after")) 1135 }) 1136 1137 It("exits early and passes when used with consistently", func() { 1138 i := 0 1139 order := []string{} 1140 Consistently(nil).Should(QuickMatcherWithOracle( 1141 func(_ any) (bool, error) { 1142 order = append(order, fmt.Sprintf("match %d", i)) 1143 i += 1 1144 if i > 4 { 1145 return false, nil 1146 } 1147 return true, nil 1148 }, 1149 func(_ any) bool { 1150 order = append(order, fmt.Sprintf("oracle %d", i)) 1151 if i == 3 { 1152 return false 1153 } 1154 return true 1155 }, 1156 )) 1157 Ω(i).Should(Equal(4)) 1158 Ω(order).Should(Equal([]string{ 1159 "oracle 0", 1160 "match 0", 1161 "oracle 1", 1162 "match 1", 1163 "oracle 2", 1164 "match 2", 1165 "oracle 3", 1166 "match 3", 1167 })) 1168 }) 1169 }) 1170 1171 Describe("The StopTrying signal - when sent by actual", func() { 1172 var i int 1173 BeforeEach(func() { 1174 i = 0 1175 }) 1176 1177 Context("when returned as an additional error argument", func() { 1178 It("stops trying and prints out the error", func() { 1179 ig.G.Eventually(func() (int, error) { 1180 i += 1 1181 if i < 3 { 1182 return i, nil 1183 } 1184 return 0, StopTrying("bam") 1185 }).Should(Equal(3)) 1186 Ω(i).Should(Equal(3)) 1187 Ω(ig.FailureMessage).Should(ContainSubstring("Told to stop trying after")) 1188 Ω(ig.FailureMessage).Should(ContainSubstring("bam")) 1189 }) 1190 1191 It("fails, even if the match were to happen to succeed", func() { 1192 ig.G.Eventually(func() (int, error) { 1193 i += 1 1194 if i < 3 { 1195 return i, nil 1196 } 1197 return i, StopTrying("bam") 1198 }).Should(Equal(3)) 1199 Ω(i).Should(Equal(3)) 1200 Ω(ig.FailureMessage).Should(ContainSubstring("Told to stop trying after")) 1201 Ω(ig.FailureMessage).Should(ContainSubstring("bam")) 1202 }) 1203 }) 1204 1205 Context("when returned as the sole actual", func() { 1206 It("stops trying and prints out the error", func() { 1207 ig.G.Eventually(func() error { 1208 i += 1 1209 if i < 3 { 1210 return errors.New("boom") 1211 } 1212 return StopTrying("bam") 1213 }).Should(Succeed()) 1214 Ω(i).Should(Equal(3)) 1215 Ω(ig.FailureMessage).Should(ContainSubstring("Told to stop trying after")) 1216 Ω(ig.FailureMessage).Should(ContainSubstring("bam")) 1217 }) 1218 }) 1219 1220 Context("when triggered via StopTrying.Now()", func() { 1221 It("stops trying and prints out the error", func() { 1222 ig.G.Eventually(func() int { 1223 i += 1 1224 if i < 3 { 1225 return i 1226 } 1227 StopTrying("bam").Now() 1228 return 0 1229 }).Should(Equal(3)) 1230 Ω(i).Should(Equal(3)) 1231 Ω(ig.FailureMessage).Should(ContainSubstring("Told to stop trying after")) 1232 Ω(ig.FailureMessage).Should(ContainSubstring("bam")) 1233 }) 1234 1235 It("works when used in conjunction with a Gomega and/or context", func() { 1236 ctx := context.WithValue(context.Background(), "key", "A") 1237 ig.G.Eventually(func(g Gomega, ctx context.Context, expected string) { 1238 i += 1 1239 if i < 3 { 1240 g.Expect(ctx.Value("key")).To(Equal(expected)) 1241 } 1242 StopTrying("Out of tries").Now() 1243 }).WithContext(ctx).WithArguments("B").Should(Succeed()) 1244 Ω(i).Should(Equal(3)) 1245 Ω(ig.FailureMessage).Should(ContainSubstring("Told to stop trying after")) 1246 Ω(ig.FailureMessage).Should(ContainSubstring("Out of tries")) 1247 }) 1248 1249 It("still allows regular panics to get through", func() { 1250 defer func() { 1251 e := recover() 1252 Ω(e).Should(Equal("welp")) 1253 }() 1254 Eventually(func() string { 1255 panic("welp") 1256 }).Should(Equal("A")) 1257 }) 1258 }) 1259 1260 Context("when used with consistently", func() { 1261 It("always signifies a failure", func() { 1262 ig.G.Consistently(func() (int, error) { 1263 i += 1 1264 if i >= 3 { 1265 return i, StopTrying("bam") 1266 } 1267 return i, nil 1268 }).Should(BeNumerically("<", 10)) 1269 Ω(i).Should(Equal(3)) 1270 Ω(ig.FailureMessage).Should(ContainSubstring("Told to stop trying after")) 1271 Ω(ig.FailureMessage).Should(ContainSubstring("bam")) 1272 }) 1273 }) 1274 1275 Context("when StopTrying has attachments", func() { 1276 It("formats them nicely", func() { 1277 type widget struct { 1278 Name string 1279 DefronculatorCount int 1280 } 1281 type sprocket struct { 1282 Type string 1283 Duration time.Duration 1284 } 1285 1286 ig.G.Eventually(func() int { 1287 StopTrying("bam").Wrap(errors.New("boom")). 1288 Attach("widget", widget{"bob", 17}). 1289 Attach("sprocket", sprocket{"james", time.Second}). 1290 Now() 1291 return 0 1292 }).Should(Equal(1)) 1293 Ω(ig.FailureMessage).Should(ContainSubstring("Told to stop trying after")) 1294 Ω(ig.FailureMessage).Should(ContainSubstring(`bam: boom 1295 widget: 1296 <internal_test.widget>: { 1297 Name: "bob", 1298 DefronculatorCount: 17, 1299 } 1300 sprocket: 1301 <internal_test.sprocket>: {Type: "james", Duration: 1000000000}`)) 1302 }) 1303 }) 1304 1305 Context("when wrapped by an outer error", func() { 1306 It("still signals as StopTrying - but the outer-error is rendered, along with any attachments", func() { 1307 ig.G.Eventually(func() error { 1308 i += 1 1309 return fmt.Errorf("wizz: %w", StopTrying("bam").Wrap(errors.New("boom")). 1310 Attach("widget", "bob"). 1311 Attach("sprocket", 17)) 1312 }).Should(Succeed()) 1313 Ω(i).Should(Equal(1)) 1314 Ω(ig.FailureMessage).ShouldNot(ContainSubstring("The function passed to")) 1315 Ω(ig.FailureMessage).Should(ContainSubstring("Told to stop trying after")) 1316 Ω(ig.FailureMessage).Should(ContainSubstring(`wizz: bam: boom 1317 widget: 1318 <string>: bob 1319 sprocket: 1320 <int>: 17`)) 1321 1322 }) 1323 }) 1324 1325 Context("when a non-PollingSignalError is in play", func() { 1326 It("also includes the format.Object representation", func() { 1327 ig.G.Eventually(func() (int, error) { 1328 return 0, fmt.Errorf("bam") 1329 }).WithTimeout(10 * time.Millisecond).Should(Equal(1)) 1330 Ω(ig.FailureMessage).Should(ContainSubstring(`{s: "bam"}`)) 1331 }) 1332 }) 1333 }) 1334 1335 Describe("The StopTrying signal - when sent by the matcher", func() { 1336 var i int 1337 BeforeEach(func() { 1338 i = 0 1339 }) 1340 1341 Context("when returned as the error", func() { 1342 It("stops retrying", func() { 1343 ig.G.Eventually(nil).Should(QuickMatcher(func(_ any) (bool, error) { 1344 i += 1 1345 if i < 3 { 1346 return false, nil 1347 } 1348 return false, StopTrying("bam") 1349 })) 1350 1351 Ω(i).Should(Equal(3)) 1352 Ω(ig.FailureMessage).Should(ContainSubstring("Told to stop trying after")) 1353 Ω(ig.FailureMessage).Should(ContainSubstring("bam")) 1354 }) 1355 1356 It("fails regardless of the matchers value", func() { 1357 ig.G.Eventually(nil).Should(QuickMatcher(func(_ any) (bool, error) { 1358 i += 1 1359 if i < 3 { 1360 return false, nil 1361 } 1362 return true, StopTrying("bam") 1363 })) 1364 1365 Ω(i).Should(Equal(3)) 1366 Ω(ig.FailureMessage).Should(ContainSubstring("Told to stop trying after")) 1367 Ω(ig.FailureMessage).Should(ContainSubstring("bam")) 1368 }) 1369 }) 1370 1371 Context("when thrown with .Now()", func() { 1372 It("stops retrying", func() { 1373 ig.G.Eventually(nil).Should(QuickMatcher(func(_ any) (bool, error) { 1374 i += 1 1375 if i < 3 { 1376 return false, nil 1377 } 1378 StopTrying("bam").Now() 1379 return false, nil 1380 })) 1381 1382 Ω(i).Should(Equal(3)) 1383 Ω(ig.FailureMessage).Should(ContainSubstring("Told to stop trying after")) 1384 Ω(ig.FailureMessage).Should(ContainSubstring("bam")) 1385 }) 1386 }) 1387 1388 Context("when used with consistently", func() { 1389 It("always signifies a failure", func() { 1390 ig.G.Consistently(nil).Should(QuickMatcher(func(_ any) (bool, error) { 1391 i += 1 1392 if i < 3 { 1393 return true, nil 1394 } 1395 return true, StopTrying("bam") 1396 })) 1397 1398 Ω(i).Should(Equal(3)) 1399 Ω(ig.FailureMessage).Should(ContainSubstring("Told to stop trying after")) 1400 Ω(ig.FailureMessage).Should(ContainSubstring("bam")) 1401 1402 }) 1403 }) 1404 1405 It("forwards any non-signaling panics", func() { 1406 defer func() { 1407 e := recover() 1408 Ω(e).Should(Equal("welp")) 1409 }() 1410 Eventually(nil).Should(QuickMatcher(func(actual any) (bool, error) { 1411 panic("welp") 1412 })) 1413 }) 1414 }) 1415 }) 1416 1417 Describe("dynamically adjusting the polling interval", func() { 1418 var i int 1419 var times []time.Duration 1420 var t time.Time 1421 1422 BeforeEach(func() { 1423 i = 0 1424 times = []time.Duration{} 1425 t = time.Now() 1426 }) 1427 1428 Context("and the assertion eventually succeeds", func() { 1429 It("adjusts the timing of the next iteration", func() { 1430 Eventually(func() error { 1431 times = append(times, time.Since(t)) 1432 t = time.Now() 1433 i += 1 1434 if i < 3 { 1435 return errors.New("stay on target") 1436 } 1437 if i == 3 { 1438 return TryAgainAfter(time.Millisecond * 200) 1439 } 1440 if i == 4 { 1441 return errors.New("you've switched off your targeting computer") 1442 } 1443 if i == 5 { 1444 TryAgainAfter(time.Millisecond * 100).Now() 1445 } 1446 if i == 6 { 1447 return errors.New("stay on target") 1448 } 1449 return nil 1450 }).ProbeEvery(time.Millisecond * 10).Should(Succeed()) 1451 Ω(i).Should(Equal(7)) 1452 Ω(times).Should(HaveLen(7)) 1453 Ω(times[0]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1454 Ω(times[1]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1455 Ω(times[2]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1456 Ω(times[3]).Should(BeNumerically("~", time.Millisecond*200, time.Millisecond*200)) 1457 Ω(times[4]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1458 Ω(times[5]).Should(BeNumerically("~", time.Millisecond*100, time.Millisecond*100)) 1459 Ω(times[6]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1460 }) 1461 }) 1462 1463 Context("and the assertion timesout while waiting", func() { 1464 It("fails with a timeout and emits the try again after error", func() { 1465 ig.G.Eventually(func() (int, error) { 1466 times = append(times, time.Since(t)) 1467 t = time.Now() 1468 i += 1 1469 if i < 3 { 1470 return i, nil 1471 } 1472 if i == 3 { 1473 return i, TryAgainAfter(time.Second * 10).Wrap(errors.New("bam")) 1474 } 1475 return i, nil 1476 }).ProbeEvery(time.Millisecond * 10).WithTimeout(time.Millisecond * 300).Should(Equal(4)) 1477 Ω(i).Should(Equal(3)) 1478 Ω(times).Should(HaveLen(3)) 1479 Ω(times[0]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1480 Ω(times[1]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1481 Ω(times[2]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1482 1483 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out after")) 1484 Ω(ig.FailureMessage).Should(ContainSubstring("told to try again after 10s: bam")) 1485 Ω(ig.FailureMessage).Should(ContainSubstring("At one point, however, the function did return successfully.\nYet, Eventually failed because the matcher was not satisfied:\nExpected\n <int>: 2\nto equal\n <int>: 4")) 1486 }) 1487 }) 1488 1489 Context("when used with Consistently", func() { 1490 It("doesn't immediately count as a failure and adjusts the timing of the next iteration", func() { 1491 Consistently(func() (int, error) { 1492 times = append(times, time.Since(t)) 1493 t = time.Now() 1494 i += 1 1495 if i == 3 { 1496 return i, TryAgainAfter(time.Millisecond * 200) 1497 } 1498 return i, nil 1499 }).ProbeEvery(time.Millisecond * 10).WithTimeout(time.Millisecond * 500).Should(BeNumerically("<", 1000)) 1500 Ω(times[0]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1501 Ω(times[1]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1502 Ω(times[2]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1503 Ω(times[3]).Should(BeNumerically("~", time.Millisecond*200, time.Millisecond*200)) 1504 Ω(times[4]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1505 }) 1506 1507 It("doesn't count as a failure if a timeout occurs during the try again after window", func() { 1508 ig.G.Consistently(func() (int, error) { 1509 times = append(times, time.Since(t)) 1510 t = time.Now() 1511 i += 1 1512 if i == 3 { 1513 return i, TryAgainAfter(time.Second * 10).Wrap(errors.New("bam")) 1514 } 1515 return i, nil 1516 }).ProbeEvery(time.Millisecond * 10).WithTimeout(time.Millisecond * 300).Should(BeNumerically("<", 1000)) 1517 Ω(times[0]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1518 Ω(times[1]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1519 Ω(times[2]).Should(BeNumerically("~", time.Millisecond*10, time.Millisecond*10)) 1520 Ω(ig.FailureMessage).Should(ContainSubstring("Timed out while waiting on TryAgainAfter after")) 1521 Ω(ig.FailureMessage).Should(ContainSubstring("told to try again after 10s: bam")) 1522 }) 1523 }) 1524 }) 1525 1526 Describe("reporting on failures in the presence of either matcher errors or actual errors", func() { 1527 When("there is no actual error or matcher error", func() { 1528 It("simply emits the correct matcher failure message", func() { 1529 ig.G.Eventually(func() (int, error) { 1530 return 5, nil 1531 }).WithTimeout(time.Millisecond*10).Should(QuickMatcher(func(actual any) (bool, error) { 1532 return false, nil 1533 }), "My Description") 1534 Ω(ig.FailureMessage).Should(HaveSuffix("My Description\nQM failure message: 5")) 1535 1536 ig.G.Eventually(func() (int, error) { 1537 return 5, nil 1538 }).WithTimeout(time.Millisecond*10).ShouldNot(QuickMatcher(func(actual any) (bool, error) { 1539 return true, nil 1540 }), "My Description") 1541 Ω(ig.FailureMessage).Should(HaveSuffix("My Description\nQM negated failure message: 5")) 1542 }) 1543 }) 1544 1545 When("there is no actual error, but there is a matcher error", func() { 1546 It("emits the matcher error", func() { 1547 ig.G.Eventually(func() (int, error) { 1548 return 5, nil 1549 }).WithTimeout(time.Millisecond*10).Should(QuickMatcher(func(actual any) (bool, error) { 1550 return false, fmt.Errorf("matcher-error") 1551 }), "My Description") 1552 Ω(ig.FailureMessage).Should(ContainSubstring("My Description\nThe matcher passed to Eventually returned the following error:\n <*errors.errorString")) 1553 Ω(ig.FailureMessage).Should(ContainSubstring("matcher-error")) 1554 }) 1555 1556 When("the matcher error is a StopTrying with attachments", func() { 1557 It("emits the error along with its attachments", func() { 1558 ig.G.Eventually(func() (int, error) { 1559 return 5, nil 1560 }).WithTimeout(time.Millisecond*10).Should(QuickMatcher(func(actual any) (bool, error) { 1561 return false, StopTrying("stop-trying").Attach("now, please", 17) 1562 }), "My Description") 1563 Ω(ig.FailureMessage).Should(HavePrefix("Told to stop trying")) 1564 Ω(ig.FailureMessage).Should(HaveSuffix("My Description\nstop-trying\nnow, please:\n <int>: 17")) 1565 }) 1566 }) 1567 }) 1568 1569 When("there is an actual error", func() { 1570 When("it never manages to get an actual", func() { 1571 It("simply emits the actual error", func() { 1572 ig.G.Eventually(func() (int, error) { 1573 return 0, fmt.Errorf("actual-err") 1574 }).WithTimeout(time.Millisecond*10).Should(QuickMatcher(func(actual any) (bool, error) { 1575 return true, nil 1576 }), "My Description") 1577 Ω(ig.FailureMessage).Should(ContainSubstring("My Description\nThe function passed to Eventually returned the following error:\n <*errors.errorString")) 1578 Ω(ig.FailureMessage).Should(ContainSubstring("actual-err")) 1579 }) 1580 }) 1581 1582 When("the actual error is because there was a non-nil/non-zero return value", func() { 1583 It("emites a clear message about the non-nil/non-zero return value", func() { 1584 ig.G.Eventually(func() (int, int, error) { 1585 return 0, 1, nil 1586 }).WithTimeout(time.Millisecond*10).Should(QuickMatcher(func(actual any) (bool, error) { 1587 return true, nil 1588 }), "My Description") 1589 Ω(ig.FailureMessage).Should(ContainSubstring("My Description\nThe function passed to Eventually had an unexpected non-nil/non-zero return value at index 1:\n <int>: 1")) 1590 }) 1591 }) 1592 1593 When("the actual error is because there was an assertion failure in the function, and there are return values", func() { 1594 It("emits a clear message about the error having occurred", func() { 1595 _, file, line, _ := runtime.Caller(0) 1596 ig.G.Eventually(func(g Gomega) int { 1597 g.Expect(true).To(BeFalse()) 1598 return 1 1599 }).WithTimeout(time.Millisecond*10).Should(QuickMatcher(func(actual any) (bool, error) { 1600 return true, nil 1601 }), "My Description") 1602 Ω(ig.FailureMessage).Should(HaveSuffix("My Description\nThe function passed to Eventually failed at %s:%d with:\nExpected\n <bool>: true\nto be false\n", file, line+2)) 1603 }) 1604 }) 1605 1606 When("the actual error is because there was an assertion failure in the function, and there are no return values", func() { 1607 It("emits a clear message about the error having occurred", func() { 1608 _, file, line, _ := runtime.Caller(0) 1609 ig.G.Eventually(func(g Gomega) { 1610 g.Expect(true).To(BeFalse()) 1611 }).WithTimeout(time.Millisecond*10).Should(Succeed(), "My Description") 1612 Ω(ig.FailureMessage).Should(HaveSuffix("My Description\nThe function passed to Eventually failed at %s:%d with:\nExpected\n <bool>: true\nto be false", file, line+2)) 1613 }) 1614 }) 1615 1616 When("it did manage to get an actual", func() { 1617 When("that actual generates a matcher error", func() { 1618 It("emits the actual error, and then emits the matcher error", func() { 1619 counter := 0 1620 ig.G.Eventually(func() (int, error) { 1621 counter += 1 1622 if counter > 3 { 1623 return counter, fmt.Errorf("actual-err") 1624 } else { 1625 return counter, nil 1626 } 1627 }).WithTimeout(time.Millisecond*100).Should(QuickMatcher(func(actual any) (bool, error) { 1628 if actual.(int) == 3 { 1629 return true, fmt.Errorf("matcher-err") 1630 } 1631 return false, nil 1632 }), "My Description") 1633 Ω(ig.FailureMessage).Should(ContainSubstring("My Description\nThe function passed to Eventually returned the following error:\n <*errors.errorString")) 1634 Ω(ig.FailureMessage).Should(ContainSubstring("actual-err")) 1635 Ω(ig.FailureMessage).Should(ContainSubstring("At one point, however, the function did return successfully.\nYet, Eventually failed because the matcher returned the following error:")) 1636 Ω(ig.FailureMessage).Should(ContainSubstring("matcher-err")) 1637 }) 1638 }) 1639 1640 When("that actual simply didn't match", func() { 1641 It("emits the matcher's failure message", func() { 1642 counter := 0 1643 ig.G.Eventually(func() (int, error) { 1644 counter += 1 1645 if counter > 3 { 1646 return counter, fmt.Errorf("actual-err") 1647 } else { 1648 return counter, nil 1649 } 1650 }).WithTimeout(time.Millisecond*100).Should(QuickMatcher(func(actual any) (bool, error) { 1651 actualInt := actual.(int) 1652 return actualInt > 3, nil 1653 }), "My Description") 1654 Ω(ig.FailureMessage).Should(ContainSubstring("My Description\nThe function passed to Eventually returned the following error:\n <*errors.errorString")) 1655 Ω(ig.FailureMessage).Should(ContainSubstring("actual-err")) 1656 Ω(ig.FailureMessage).Should(ContainSubstring("At one point, however, the function did return successfully.\nYet, Eventually failed because the matcher was not satisfied:\nQM failure message: 3")) 1657 1658 }) 1659 }) 1660 }) 1661 }) 1662 }) 1663 1664 When("vetting optional description parameters", func() { 1665 It("panics when Gomega matcher is at the beginning of optional description parameters", func() { 1666 ig := NewInstrumentedGomega() 1667 for _, expectator := range []string{ 1668 "Should", "ShouldNot", 1669 } { 1670 Expect(func() { 1671 eventually := ig.G.Eventually(42) // sic! 1672 meth := reflect.ValueOf(eventually).MethodByName(expectator) 1673 Expect(meth.IsValid()).To(BeTrue()) 1674 meth.Call([]reflect.Value{ 1675 reflect.ValueOf(HaveLen(1)), 1676 reflect.ValueOf(ContainElement(42)), 1677 }) 1678 }).To(PanicWith(MatchRegexp("Asynchronous assertion has a GomegaMatcher as the first element of optionalDescription"))) 1679 } 1680 }) 1681 1682 It("accepts Gomega matchers in optional description parameters after the first", func() { 1683 Expect(func() { 1684 ig := NewInstrumentedGomega() 1685 ig.G.Eventually(42).Should(HaveLen(1), "foo", ContainElement(42)) 1686 }).NotTo(Panic()) 1687 }) 1688 }) 1689 1690 Context("eventual nil-ism", func() { // issue #555 1691 It("doesn't panic on nil actual", func() { 1692 ig := NewInstrumentedGomega() 1693 Expect(func() { 1694 ig.G.Eventually(nil).Should(BeNil()) 1695 }).NotTo(Panic()) 1696 }) 1697 1698 It("doesn't panic on function returning nil error", func() { 1699 ig := NewInstrumentedGomega() 1700 Expect(func() { 1701 ig.G.Eventually(func() error { return nil }).Should(BeNil()) 1702 }).NotTo(Panic()) 1703 }) 1704 }) 1705 1706 When("using MustPassRepeatedly", func() { 1707 It("errors when using on Consistently", func() { 1708 ig.G.Consistently(func(g Gomega) {}).MustPassRepeatedly(2).Should(Succeed()) 1709 Ω(ig.FailureMessage).Should(ContainSubstring("Invalid use of MustPassRepeatedly with Consistently it can only be used with Eventually")) 1710 Ω(ig.FailureSkip).Should(Equal([]int{2})) 1711 }) 1712 It("errors when using with 0", func() { 1713 ig.G.Eventually(func(g Gomega) {}).MustPassRepeatedly(0).Should(Succeed()) 1714 Ω(ig.FailureMessage).Should(ContainSubstring("Invalid use of MustPassRepeatedly with Eventually parameter can't be < 1")) 1715 Ω(ig.FailureSkip).Should(Equal([]int{2})) 1716 }) 1717 1718 It("should wait 2 success before success", func() { 1719 counter := 0 1720 ig.G.Eventually(func() bool { 1721 counter++ 1722 return counter > 5 1723 }).MustPassRepeatedly(2).Should(BeTrue()) 1724 Ω(counter).Should(Equal(7)) 1725 Ω(ig.FailureMessage).Should(BeZero()) 1726 }) 1727 1728 It("should fail if it never succeeds twice in a row", func() { 1729 counter := 0 1730 ig.G.Eventually(func() int { 1731 counter++ 1732 return counter % 2 1733 }).WithTimeout(200 * time.Millisecond).WithPolling(20 * time.Millisecond).MustPassRepeatedly(2).Should(Equal(1)) 1734 Ω(counter).Should(Equal(10)) 1735 Ω(ig.FailureMessage).ShouldNot(BeZero()) 1736 }) 1737 1738 It("TryAgainAfter doesn't restore count", func() { 1739 counter := 0 1740 ig.G.Eventually(func() (bool, error) { 1741 counter++ 1742 if counter == 5 { 1743 return false, TryAgainAfter(time.Millisecond * 200) 1744 } 1745 return counter >= 4, nil 1746 }).MustPassRepeatedly(3).Should(BeTrue()) 1747 Ω(counter).Should(Equal(7)) 1748 Ω(ig.FailureMessage).Should(BeZero()) 1749 }) 1750 1751 }) 1752 })