@@ -2,6 +2,7 @@ package internal
22
33import (
44 "context"
5+ "errors"
56 "fmt"
67 "reflect"
78 "runtime"
@@ -16,6 +17,22 @@ var errInterface = reflect.TypeOf((*error)(nil)).Elem()
1617var gomegaType = reflect .TypeOf ((* types .Gomega )(nil )).Elem ()
1718var contextType = reflect .TypeOf (new (context.Context )).Elem ()
1819
20+ type formattedGomegaError interface {
21+ FormattedGomegaError () string
22+ }
23+
24+ type asyncPolledActualError struct {
25+ message string
26+ }
27+
28+ func (err * asyncPolledActualError ) Error () string {
29+ return err .message
30+ }
31+
32+ func (err * asyncPolledActualError ) FormattedGomegaError () string {
33+ return err .message
34+ }
35+
1936type contextWithAttachProgressReporter interface {
2037 AttachProgressReporter (func () string ) func ()
2138}
@@ -148,7 +165,9 @@ func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interfa
148165
149166func (assertion * AsyncAssertion ) processReturnValues (values []reflect.Value ) (interface {}, error ) {
150167 if len (values ) == 0 {
151- return nil , fmt .Errorf ("No values were returned by the function passed to Gomega" )
168+ return nil , & asyncPolledActualError {
169+ message : fmt .Sprintf ("The function passed to %s did not return any values" , assertion .asyncType ),
170+ }
152171 }
153172
154173 actual := values [0 ].Interface ()
@@ -171,10 +190,12 @@ func (assertion *AsyncAssertion) processReturnValues(values []reflect.Value) (in
171190 continue
172191 }
173192 if i == len (values )- 2 && extraType .Implements (errInterface ) {
174- err = fmt . Errorf ( "function returned error: %w" , extra .(error ) )
193+ err = extra .(error )
175194 }
176195 if err == nil {
177- err = fmt .Errorf ("Unexpected non-nil/non-zero return value at index %d:\n \t <%T>: %#v" , i + 1 , extra , extra )
196+ err = & asyncPolledActualError {
197+ message : fmt .Sprintf ("The function passed to %s had an unexpected non-nil/non-zero return value at index %d:\n %s" , assertion .asyncType , i + 1 , format .Object (extra , 1 )),
198+ }
178199 }
179200 }
180201
@@ -253,7 +274,9 @@ func (assertion *AsyncAssertion) buildActualPoller() (func() (interface{}, error
253274 skip = callerSkip [0 ]
254275 }
255276 _ , file , line , _ := runtime .Caller (skip + 1 )
256- assertionFailure = fmt .Errorf ("Assertion in callback at %s:%d failed:\n %s" , file , line , message )
277+ assertionFailure = & asyncPolledActualError {
278+ message : fmt .Sprintf ("The function passed to %s failed at %s:%d with:\n %s" , assertion .asyncType , file , line , message ),
279+ }
257280 // we throw an asyncGomegaHaltExecutionError so that defer GinkgoRecover() can catch this error if the user makes an assertion in a goroutine
258281 panic (asyncGomegaHaltExecutionError {})
259282 })))
@@ -359,46 +382,85 @@ func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch
359382 timeout := assertion .afterTimeout ()
360383 lock := sync.Mutex {}
361384
362- var matches bool
363- var err error
385+ var matches , hasLastValidActual bool
386+ var actual , lastValidActual interface {}
387+ var actualErr , matcherErr error
364388 var oracleMatcherSaysStop bool
365389
366390 assertion .g .THelper ()
367391
368- pollActual , err := assertion .buildActualPoller ()
369- if err != nil {
370- assertion .g .Fail (err .Error (), 2 + assertion .offset )
392+ pollActual , buildActualPollerErr := assertion .buildActualPoller ()
393+ if buildActualPollerErr != nil {
394+ assertion .g .Fail (buildActualPollerErr .Error (), 2 + assertion .offset )
371395 return false
372396 }
373397
374- value , err := pollActual ()
375- if err == nil {
376- oracleMatcherSaysStop = assertion .matcherSaysStopTrying (matcher , value )
377- matches , err = assertion .pollMatcher (matcher , value )
398+ actual , actualErr = pollActual ()
399+ if actualErr == nil {
400+ lastValidActual = actual
401+ hasLastValidActual = true
402+ oracleMatcherSaysStop = assertion .matcherSaysStopTrying (matcher , actual )
403+ matches , matcherErr = assertion .pollMatcher (matcher , actual )
404+ }
405+
406+ renderError := func (preamble string , err error ) string {
407+ message := ""
408+ if pollingSignalErr , ok := AsPollingSignalError (err ); ok {
409+ message = err .Error ()
410+ for _ , attachment := range pollingSignalErr .Attachments {
411+ message += fmt .Sprintf ("\n %s:\n " , attachment .Description )
412+ message += format .Object (attachment .Object , 1 )
413+ }
414+ } else {
415+ message = preamble + "\n " + err .Error () + "\n " + format .Object (err , 1 )
416+ }
417+ return message
378418 }
379419
380420 messageGenerator := func () string {
381421 // can be called out of band by Ginkgo if the user requests a progress report
382422 lock .Lock ()
383423 defer lock .Unlock ()
384424 message := ""
385- if err != nil {
386- if pollingSignalErr , ok := AsPollingSignalError (err ); ok && pollingSignalErr .IsStopTrying () {
387- message = err .Error ()
388- for _ , attachment := range pollingSignalErr .Attachments {
389- message += fmt .Sprintf ("\n %s:\n " , attachment .Description )
390- message += format .Object (attachment .Object , 1 )
425+
426+ if actualErr == nil {
427+ if matcherErr == nil {
428+ if desiredMatch {
429+ message += matcher .FailureMessage (actual )
430+ } else {
431+ message += matcher .NegatedFailureMessage (actual )
391432 }
392433 } else {
393- message = "Error: " + err .Error () + "\n " + format .Object (err , 1 )
434+ var fgErr formattedGomegaError
435+ if errors .As (actualErr , & fgErr ) {
436+ message += fgErr .FormattedGomegaError () + "\n "
437+ } else {
438+ message += renderError (fmt .Sprintf ("The matcher passed to %s returned the following error:" , assertion .asyncType ), matcherErr )
439+ }
394440 }
395441 } else {
396- if desiredMatch {
397- message = matcher .FailureMessage (value )
442+ var fgErr formattedGomegaError
443+ if errors .As (actualErr , & fgErr ) {
444+ message += fgErr .FormattedGomegaError () + "\n "
398445 } else {
399- message = matcher .NegatedFailureMessage (value )
446+ message += renderError (fmt .Sprintf ("The function passed to %s returned the following error:" , assertion .asyncType ), actualErr )
447+ }
448+ if hasLastValidActual {
449+ message += fmt .Sprintf ("\n At one point, however, the function did return successfully. But %s failed because" , assertion .asyncType )
450+ _ , e := matcher .Match (lastValidActual )
451+ if e != nil {
452+ message += renderError (" the matcher returned the following error:" , e )
453+ } else {
454+ message += " the matcher was not satisfied:\n "
455+ if desiredMatch {
456+ message += matcher .FailureMessage (lastValidActual )
457+ } else {
458+ message += matcher .NegatedFailureMessage (lastValidActual )
459+ }
460+ }
400461 }
401462 }
463+
402464 description := assertion .buildDescription (optionalDescription ... )
403465 return fmt .Sprintf ("%s%s" , description , message )
404466 }
@@ -423,18 +485,20 @@ func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch
423485 var nextPoll <- chan time.Time = nil
424486 var isTryAgainAfterError = false
425487
426- if pollingSignalErr , ok := AsPollingSignalError (err ); ok {
427- if pollingSignalErr .IsStopTrying () {
428- fail ("Told to stop trying" )
429- return false
430- }
431- if pollingSignalErr .IsTryAgainAfter () {
432- nextPoll = time .After (pollingSignalErr .TryAgainDuration ())
433- isTryAgainAfterError = true
488+ for _ , err := range []error {actualErr , matcherErr } {
489+ if pollingSignalErr , ok := AsPollingSignalError (err ); ok {
490+ if pollingSignalErr .IsStopTrying () {
491+ fail ("Told to stop trying" )
492+ return false
493+ }
494+ if pollingSignalErr .IsTryAgainAfter () {
495+ nextPoll = time .After (pollingSignalErr .TryAgainDuration ())
496+ isTryAgainAfterError = true
497+ }
434498 }
435499 }
436500
437- if err == nil && matches == desiredMatch {
501+ if actualErr == nil && matcherErr == nil && matches == desiredMatch {
438502 if assertion .asyncType == AsyncAssertionTypeEventually {
439503 passedRepeatedlyCount += 1
440504 if passedRepeatedlyCount == assertion .mustPassRepeatedly {
@@ -465,15 +529,19 @@ func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch
465529
466530 select {
467531 case <- nextPoll :
468- v , e := pollActual ()
532+ a , e := pollActual ()
469533 lock .Lock ()
470- value , err = v , e
534+ actual , actualErr = a , e
471535 lock .Unlock ()
472- if err == nil {
473- oracleMatcherSaysStop = assertion .matcherSaysStopTrying (matcher , value )
474- m , e := assertion .pollMatcher (matcher , value )
536+ if actualErr == nil {
537+ lock .Lock ()
538+ lastValidActual = actual
539+ hasLastValidActual = true
540+ lock .Unlock ()
541+ oracleMatcherSaysStop = assertion .matcherSaysStopTrying (matcher , actual )
542+ m , e := assertion .pollMatcher (matcher , actual )
475543 lock .Lock ()
476- matches , err = m , e
544+ matches , matcherErr = m , e
477545 lock .Unlock ()
478546 }
479547 case <- contextDone :
0 commit comments