...

Source file src/github.com/onsi/gomega/internal/async_assertion_test.go

Documentation: github.com/onsi/gomega/internal

     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  })
  1753  

View as plain text