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).
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")
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))
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")
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")
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")
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)
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() {
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