2021-09-01 13:50:18 +00:00
package internal
2019-05-31 09:45:11 +00:00
import (
2022-10-17 17:38:08 +00:00
"context"
2023-02-01 12:50:52 +00:00
"errors"
2019-05-31 09:45:11 +00:00
"fmt"
"reflect"
2021-09-01 13:50:18 +00:00
"runtime"
2022-10-17 17:38:08 +00:00
"sync"
2019-05-31 09:45:11 +00:00
"time"
2022-11-01 15:23:27 +00:00
"github.com/onsi/gomega/format"
2019-05-31 09:45:11 +00:00
"github.com/onsi/gomega/types"
)
2022-11-01 15:23:27 +00:00
var errInterface = reflect . TypeOf ( ( * error ) ( nil ) ) . Elem ( )
var gomegaType = reflect . TypeOf ( ( * types . Gomega ) ( nil ) ) . Elem ( )
var contextType = reflect . TypeOf ( new ( context . Context ) ) . Elem ( )
2022-10-17 17:38:08 +00:00
2023-02-01 12:50:52 +00:00
type formattedGomegaError interface {
FormattedGomegaError ( ) string
}
type asyncPolledActualError struct {
message string
}
func ( err * asyncPolledActualError ) Error ( ) string {
return err . message
}
func ( err * asyncPolledActualError ) FormattedGomegaError ( ) string {
return err . message
}
2022-11-01 15:23:27 +00:00
type contextWithAttachProgressReporter interface {
AttachProgressReporter ( func ( ) string ) func ( )
2022-10-17 17:38:08 +00:00
}
2023-01-30 20:05:01 +00:00
type asyncGomegaHaltExecutionError struct { }
func ( a asyncGomegaHaltExecutionError ) GinkgoRecoverShouldIgnoreThisPanic ( ) { }
func ( a asyncGomegaHaltExecutionError ) Error ( ) string {
return ` An assertion has failed in a goroutine . You should call
defer GinkgoRecover ( )
at the top of the goroutine that caused this panic . This will allow Ginkgo and Gomega to correctly capture and manage this panic . `
}
2019-05-31 09:45:11 +00:00
type AsyncAssertionType uint
const (
AsyncAssertionTypeEventually AsyncAssertionType = iota
AsyncAssertionTypeConsistently
)
2022-10-17 17:38:08 +00:00
func ( at AsyncAssertionType ) String ( ) string {
switch at {
case AsyncAssertionTypeEventually :
return "Eventually"
case AsyncAssertionTypeConsistently :
return "Consistently"
}
return "INVALID ASYNC ASSERTION TYPE"
}
2019-05-31 09:45:11 +00:00
type AsyncAssertion struct {
2021-09-01 13:50:18 +00:00
asyncType AsyncAssertionType
2022-10-17 17:38:08 +00:00
actualIsFunc bool
actual interface { }
argsToForward [ ] interface { }
2021-09-01 13:50:18 +00:00
2023-01-30 20:05:01 +00:00
timeoutInterval time . Duration
pollingInterval time . Duration
mustPassRepeatedly int
ctx context . Context
offset int
g * Gomega
2019-05-31 09:45:11 +00:00
}
2023-01-30 20:05:01 +00:00
func NewAsyncAssertion ( asyncType AsyncAssertionType , actualInput interface { } , g * Gomega , timeoutInterval time . Duration , pollingInterval time . Duration , mustPassRepeatedly int , ctx context . Context , offset int ) * AsyncAssertion {
2021-09-01 13:50:18 +00:00
out := & AsyncAssertion {
2023-01-30 20:05:01 +00:00
asyncType : asyncType ,
timeoutInterval : timeoutInterval ,
pollingInterval : pollingInterval ,
mustPassRepeatedly : mustPassRepeatedly ,
offset : offset ,
ctx : ctx ,
g : g ,
2021-09-01 13:50:18 +00:00
}
2022-10-17 17:38:08 +00:00
out . actual = actualInput
if actualInput != nil && reflect . TypeOf ( actualInput ) . Kind ( ) == reflect . Func {
2021-09-01 13:50:18 +00:00
out . actualIsFunc = true
2019-05-31 09:45:11 +00:00
}
2021-09-01 13:50:18 +00:00
return out
2019-05-31 09:45:11 +00:00
}
2021-11-08 20:26:59 +00:00
func ( assertion * AsyncAssertion ) WithOffset ( offset int ) types . AsyncAssertion {
assertion . offset = offset
return assertion
}
func ( assertion * AsyncAssertion ) WithTimeout ( interval time . Duration ) types . AsyncAssertion {
assertion . timeoutInterval = interval
return assertion
}
func ( assertion * AsyncAssertion ) WithPolling ( interval time . Duration ) types . AsyncAssertion {
assertion . pollingInterval = interval
return assertion
}
2022-10-17 17:38:08 +00:00
func ( assertion * AsyncAssertion ) Within ( timeout time . Duration ) types . AsyncAssertion {
assertion . timeoutInterval = timeout
return assertion
}
func ( assertion * AsyncAssertion ) ProbeEvery ( interval time . Duration ) types . AsyncAssertion {
assertion . pollingInterval = interval
return assertion
}
func ( assertion * AsyncAssertion ) WithContext ( ctx context . Context ) types . AsyncAssertion {
assertion . ctx = ctx
return assertion
}
func ( assertion * AsyncAssertion ) WithArguments ( argsToForward ... interface { } ) types . AsyncAssertion {
assertion . argsToForward = argsToForward
return assertion
}
2023-01-30 20:05:01 +00:00
func ( assertion * AsyncAssertion ) MustPassRepeatedly ( count int ) types . AsyncAssertion {
assertion . mustPassRepeatedly = count
return assertion
}
2019-05-31 09:45:11 +00:00
func ( assertion * AsyncAssertion ) Should ( matcher types . GomegaMatcher , optionalDescription ... interface { } ) bool {
2021-09-01 13:50:18 +00:00
assertion . g . THelper ( )
2022-09-05 20:11:08 +00:00
vetOptionalDescription ( "Asynchronous assertion" , optionalDescription ... )
2019-05-31 09:45:11 +00:00
return assertion . match ( matcher , true , optionalDescription ... )
}
func ( assertion * AsyncAssertion ) ShouldNot ( matcher types . GomegaMatcher , optionalDescription ... interface { } ) bool {
2021-09-01 13:50:18 +00:00
assertion . g . THelper ( )
2022-09-05 20:11:08 +00:00
vetOptionalDescription ( "Asynchronous assertion" , optionalDescription ... )
2019-05-31 09:45:11 +00:00
return assertion . match ( matcher , false , optionalDescription ... )
}
func ( assertion * AsyncAssertion ) buildDescription ( optionalDescription ... interface { } ) string {
switch len ( optionalDescription ) {
case 0 :
return ""
2020-01-14 10:38:55 +00:00
case 1 :
if describe , ok := optionalDescription [ 0 ] . ( func ( ) string ) ; ok {
return describe ( ) + "\n"
}
2019-05-31 09:45:11 +00:00
}
2020-01-14 10:38:55 +00:00
return fmt . Sprintf ( optionalDescription [ 0 ] . ( string ) , optionalDescription [ 1 : ] ... ) + "\n"
2019-05-31 09:45:11 +00:00
}
2022-11-01 15:23:27 +00:00
func ( assertion * AsyncAssertion ) processReturnValues ( values [ ] reflect . Value ) ( interface { } , error ) {
2022-10-17 17:38:08 +00:00
if len ( values ) == 0 {
2023-02-01 12:50:52 +00:00
return nil , & asyncPolledActualError {
message : fmt . Sprintf ( "The function passed to %s did not return any values" , assertion . asyncType ) ,
}
2022-10-17 17:38:08 +00:00
}
2022-11-01 15:23:27 +00:00
2022-10-17 17:38:08 +00:00
actual := values [ 0 ] . Interface ( )
2022-11-01 15:23:27 +00:00
if _ , ok := AsPollingSignalError ( actual ) ; ok {
return actual , actual . ( error )
2022-10-17 17:38:08 +00:00
}
2022-11-01 15:23:27 +00:00
var err error
2022-10-17 17:38:08 +00:00
for i , extraValue := range values [ 1 : ] {
extra := extraValue . Interface ( )
if extra == nil {
continue
}
2022-11-01 15:23:27 +00:00
if _ , ok := AsPollingSignalError ( extra ) ; ok {
return actual , extra . ( error )
2022-10-17 17:38:08 +00:00
}
2022-11-01 15:23:27 +00:00
extraType := reflect . TypeOf ( extra )
zero := reflect . Zero ( extraType ) . Interface ( )
2022-10-17 17:38:08 +00:00
if reflect . DeepEqual ( extra , zero ) {
continue
}
2022-11-01 15:23:27 +00:00
if i == len ( values ) - 2 && extraType . Implements ( errInterface ) {
2023-02-01 12:50:52 +00:00
err = extra . ( error )
2022-11-01 15:23:27 +00:00
}
2022-10-17 17:38:08 +00:00
if err == nil {
2023-02-01 12:50:52 +00:00
err = & asyncPolledActualError {
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 ) ) ,
}
2022-10-17 17:38:08 +00:00
}
}
2022-11-01 15:23:27 +00:00
return actual , err
}
2022-10-17 17:38:08 +00:00
func ( assertion * AsyncAssertion ) invalidFunctionError ( t reflect . Type ) error {
return fmt . Errorf ( ` The function passed to % s had an invalid signature of % s . Functions passed to % s must either :
( a ) have return values or
( b ) take a Gomega interface as their first argument and use that Gomega instance to make assertions .
You can learn more at https : //onsi.github.io/gomega/#eventually
` , assertion . asyncType , t , assertion . asyncType )
}
func ( assertion * AsyncAssertion ) noConfiguredContextForFunctionError ( ) error {
return fmt . Errorf ( ` The function passed to % s requested a context . Context , but no context has been provided . Please pass one in using % s ( ) . WithContext ( ) .
You can learn more at https : //onsi.github.io/gomega/#eventually
` , assertion . asyncType , assertion . asyncType )
}
func ( assertion * AsyncAssertion ) argumentMismatchError ( t reflect . Type , numProvided int ) error {
have := "have"
if numProvided == 1 {
have = "has"
}
return fmt . Errorf ( ` The function passed to % s has signature % s takes % d arguments but % d % s been provided . Please use % s ( ) . WithArguments ( ) to pass the corect set of arguments .
You can learn more at https : //onsi.github.io/gomega/#eventually
` , assertion . asyncType , t , t . NumIn ( ) , numProvided , have , assertion . asyncType )
}
2023-01-30 20:05:01 +00:00
func ( assertion * AsyncAssertion ) invalidMustPassRepeatedlyError ( reason string ) error {
return fmt . Errorf ( ` Invalid use of MustPassRepeatedly with % s % s
You can learn more at https : //onsi.github.io/gomega/#eventually
` , assertion . asyncType , reason )
}
2022-11-01 15:23:27 +00:00
func ( assertion * AsyncAssertion ) buildActualPoller ( ) ( func ( ) ( interface { } , error ) , error ) {
2021-09-01 13:50:18 +00:00
if ! assertion . actualIsFunc {
2022-11-01 15:23:27 +00:00
return func ( ) ( interface { } , error ) { return assertion . actual , nil } , nil
2021-09-01 13:50:18 +00:00
}
2022-10-17 17:38:08 +00:00
actualValue := reflect . ValueOf ( assertion . actual )
actualType := reflect . TypeOf ( assertion . actual )
numIn , numOut , isVariadic := actualType . NumIn ( ) , actualType . NumOut ( ) , actualType . IsVariadic ( )
if numIn == 0 && numOut == 0 {
return nil , assertion . invalidFunctionError ( actualType )
}
2022-11-01 15:23:27 +00:00
takesGomega , takesContext := false , false
if numIn > 0 {
takesGomega , takesContext = actualType . In ( 0 ) . Implements ( gomegaType ) , actualType . In ( 0 ) . Implements ( contextType )
}
2022-10-17 17:38:08 +00:00
if takesGomega && numIn > 1 && actualType . In ( 1 ) . Implements ( contextType ) {
takesContext = true
}
if takesContext && len ( assertion . argsToForward ) > 0 && reflect . TypeOf ( assertion . argsToForward [ 0 ] ) . Implements ( contextType ) {
takesContext = false
}
if ! takesGomega && numOut == 0 {
return nil , assertion . invalidFunctionError ( actualType )
}
if takesContext && assertion . ctx == nil {
return nil , assertion . noConfiguredContextForFunctionError ( )
}
var assertionFailure error
inValues := [ ] reflect . Value { }
if takesGomega {
inValues = append ( inValues , reflect . ValueOf ( NewGomega ( assertion . g . DurationBundle ) . ConfigureWithFailHandler ( func ( message string , callerSkip ... int ) {
skip := 0
if len ( callerSkip ) > 0 {
skip = callerSkip [ 0 ]
}
_ , file , line , _ := runtime . Caller ( skip + 1 )
2023-02-01 12:50:52 +00:00
assertionFailure = & asyncPolledActualError {
message : fmt . Sprintf ( "The function passed to %s failed at %s:%d with:\n%s" , assertion . asyncType , file , line , message ) ,
}
2023-01-30 20:05:01 +00:00
// we throw an asyncGomegaHaltExecutionError so that defer GinkgoRecover() can catch this error if the user makes an assertion in a goroutine
panic ( asyncGomegaHaltExecutionError { } )
2022-10-17 17:38:08 +00:00
} ) ) )
}
if takesContext {
inValues = append ( inValues , reflect . ValueOf ( assertion . ctx ) )
2021-09-01 13:50:18 +00:00
}
2022-10-17 17:38:08 +00:00
for _ , arg := range assertion . argsToForward {
inValues = append ( inValues , reflect . ValueOf ( arg ) )
2021-09-01 13:50:18 +00:00
}
2022-10-17 17:38:08 +00:00
if ! isVariadic && numIn != len ( inValues ) {
return nil , assertion . argumentMismatchError ( actualType , len ( inValues ) )
} else if isVariadic && len ( inValues ) < numIn - 1 {
return nil , assertion . argumentMismatchError ( actualType , len ( inValues ) )
2019-05-31 09:45:11 +00:00
}
2023-01-30 20:05:01 +00:00
if assertion . mustPassRepeatedly != 1 && assertion . asyncType != AsyncAssertionTypeEventually {
return nil , assertion . invalidMustPassRepeatedlyError ( "it can only be used with Eventually" )
}
if assertion . mustPassRepeatedly < 1 {
return nil , assertion . invalidMustPassRepeatedlyError ( "parameter can't be < 1" )
}
2022-11-01 15:23:27 +00:00
return func ( ) ( actual interface { } , err error ) {
2022-10-17 17:38:08 +00:00
var values [ ] reflect . Value
assertionFailure = nil
defer func ( ) {
2022-11-01 15:23:27 +00:00
if numOut == 0 && takesGomega {
2022-10-17 17:38:08 +00:00
actual = assertionFailure
} else {
2022-11-01 15:23:27 +00:00
actual , err = assertion . processReturnValues ( values )
_ , isAsyncError := AsPollingSignalError ( err )
if assertionFailure != nil && ! isAsyncError {
2022-10-17 17:38:08 +00:00
err = assertionFailure
}
}
if e := recover ( ) ; e != nil {
2022-11-01 15:23:27 +00:00
if _ , isAsyncError := AsPollingSignalError ( e ) ; isAsyncError {
err = e . ( error )
2022-10-17 17:38:08 +00:00
} else if assertionFailure == nil {
panic ( e )
}
}
} ( )
values = actualValue . Call ( inValues )
return
} , nil
2019-05-31 09:45:11 +00:00
}
2022-10-20 09:06:44 +00:00
func ( assertion * AsyncAssertion ) afterTimeout ( ) <- chan time . Time {
if assertion . timeoutInterval >= 0 {
return time . After ( assertion . timeoutInterval )
}
if assertion . asyncType == AsyncAssertionTypeConsistently {
return time . After ( assertion . g . DurationBundle . ConsistentlyDuration )
} else {
2024-11-04 20:10:45 +00:00
if assertion . ctx == nil || assertion . g . DurationBundle . EnforceDefaultTimeoutsWhenUsingContexts {
2022-10-20 09:06:44 +00:00
return time . After ( assertion . g . DurationBundle . EventuallyTimeout )
} else {
return nil
}
}
}
func ( assertion * AsyncAssertion ) afterPolling ( ) <- chan time . Time {
if assertion . pollingInterval >= 0 {
return time . After ( assertion . pollingInterval )
}
if assertion . asyncType == AsyncAssertionTypeConsistently {
return time . After ( assertion . g . DurationBundle . ConsistentlyPollingInterval )
} else {
return time . After ( assertion . g . DurationBundle . EventuallyPollingInterval )
}
}
2022-11-01 15:23:27 +00:00
func ( assertion * AsyncAssertion ) matcherSaysStopTrying ( matcher types . GomegaMatcher , value interface { } ) bool {
if assertion . actualIsFunc || types . MatchMayChangeInTheFuture ( matcher , value ) {
return false
}
return true
}
func ( assertion * AsyncAssertion ) pollMatcher ( matcher types . GomegaMatcher , value interface { } ) ( matches bool , err error ) {
defer func ( ) {
if e := recover ( ) ; e != nil {
if _ , isAsyncError := AsPollingSignalError ( e ) ; isAsyncError {
err = e . ( error )
} else {
panic ( e )
}
}
} ( )
matches , err = matcher . Match ( value )
return
2019-05-31 09:45:11 +00:00
}
func ( assertion * AsyncAssertion ) match ( matcher types . GomegaMatcher , desiredMatch bool , optionalDescription ... interface { } ) bool {
timer := time . Now ( )
2022-10-20 09:06:44 +00:00
timeout := assertion . afterTimeout ( )
2022-10-17 17:38:08 +00:00
lock := sync . Mutex { }
2019-05-31 09:45:11 +00:00
2023-02-01 12:50:52 +00:00
var matches , hasLastValidActual bool
var actual , lastValidActual interface { }
var actualErr , matcherErr error
2022-11-01 15:23:27 +00:00
var oracleMatcherSaysStop bool
2022-10-17 17:38:08 +00:00
assertion . g . THelper ( )
2023-02-01 12:50:52 +00:00
pollActual , buildActualPollerErr := assertion . buildActualPoller ( )
if buildActualPollerErr != nil {
assertion . g . Fail ( buildActualPollerErr . Error ( ) , 2 + assertion . offset )
2022-10-17 17:38:08 +00:00
return false
}
2023-02-01 12:50:52 +00:00
actual , actualErr = pollActual ( )
if actualErr == nil {
lastValidActual = actual
hasLastValidActual = true
oracleMatcherSaysStop = assertion . matcherSaysStopTrying ( matcher , actual )
matches , matcherErr = assertion . pollMatcher ( matcher , actual )
}
renderError := func ( preamble string , err error ) string {
message := ""
if pollingSignalErr , ok := AsPollingSignalError ( err ) ; ok {
message = err . Error ( )
for _ , attachment := range pollingSignalErr . Attachments {
message += fmt . Sprintf ( "\n%s:\n" , attachment . Description )
message += format . Object ( attachment . Object , 1 )
}
} else {
2023-03-13 21:04:21 +00:00
message = preamble + "\n" + format . Object ( err , 1 )
2023-02-01 12:50:52 +00:00
}
return message
2019-05-31 09:45:11 +00:00
}
2022-10-17 17:38:08 +00:00
messageGenerator := func ( ) string {
// can be called out of band by Ginkgo if the user requests a progress report
lock . Lock ( )
defer lock . Unlock ( )
2019-05-31 09:45:11 +00:00
message := ""
2023-02-01 12:50:52 +00:00
if actualErr == nil {
if matcherErr == nil {
2023-03-13 21:04:21 +00:00
if desiredMatch != matches {
if desiredMatch {
message += matcher . FailureMessage ( actual )
} else {
message += matcher . NegatedFailureMessage ( actual )
}
2023-02-01 12:50:52 +00:00
} else {
2023-03-13 21:04:21 +00:00
if assertion . asyncType == AsyncAssertionTypeConsistently {
message += "There is no failure as the matcher passed to Consistently has not yet failed"
} else {
message += "There is no failure as the matcher passed to Eventually succeeded on its most recent iteration"
}
2022-11-01 15:23:27 +00:00
}
} else {
2023-02-01 12:50:52 +00:00
var fgErr formattedGomegaError
if errors . As ( actualErr , & fgErr ) {
message += fgErr . FormattedGomegaError ( ) + "\n"
} else {
message += renderError ( fmt . Sprintf ( "The matcher passed to %s returned the following error:" , assertion . asyncType ) , matcherErr )
}
2022-11-01 15:23:27 +00:00
}
2019-05-31 09:45:11 +00:00
} else {
2023-02-01 12:50:52 +00:00
var fgErr formattedGomegaError
if errors . As ( actualErr , & fgErr ) {
message += fgErr . FormattedGomegaError ( ) + "\n"
2019-05-31 09:45:11 +00:00
} else {
2023-02-01 12:50:52 +00:00
message += renderError ( fmt . Sprintf ( "The function passed to %s returned the following error:" , assertion . asyncType ) , actualErr )
}
if hasLastValidActual {
message += fmt . Sprintf ( "\nAt one point, however, the function did return successfully.\nYet, %s failed because" , assertion . asyncType )
_ , e := matcher . Match ( lastValidActual )
if e != nil {
message += renderError ( " the matcher returned the following error:" , e )
} else {
message += " the matcher was not satisfied:\n"
if desiredMatch {
message += matcher . FailureMessage ( lastValidActual )
} else {
message += matcher . NegatedFailureMessage ( lastValidActual )
}
}
2019-05-31 09:45:11 +00:00
}
}
2023-02-01 12:50:52 +00:00
2020-01-14 10:38:55 +00:00
description := assertion . buildDescription ( optionalDescription ... )
2022-11-01 15:23:27 +00:00
return fmt . Sprintf ( "%s%s" , description , message )
2022-10-17 17:38:08 +00:00
}
fail := func ( preamble string ) {
assertion . g . THelper ( )
assertion . g . Fail ( fmt . Sprintf ( "%s after %.3fs.\n%s" , preamble , time . Since ( timer ) . Seconds ( ) , messageGenerator ( ) ) , 3 + assertion . offset )
}
var contextDone <- chan struct { }
if assertion . ctx != nil {
contextDone = assertion . ctx . Done ( )
if v , ok := assertion . ctx . Value ( "GINKGO_SPEC_CONTEXT" ) . ( contextWithAttachProgressReporter ) ; ok {
detach := v . AttachProgressReporter ( messageGenerator )
defer detach ( )
}
2019-05-31 09:45:11 +00:00
}
2023-01-30 20:05:01 +00:00
// Used to count the number of times in a row a step passed
passedRepeatedlyCount := 0
2022-11-01 15:23:27 +00:00
for {
var nextPoll <- chan time . Time = nil
var isTryAgainAfterError = false
2019-05-31 09:45:11 +00:00
2023-02-01 12:50:52 +00:00
for _ , err := range [ ] error { actualErr , matcherErr } {
if pollingSignalErr , ok := AsPollingSignalError ( err ) ; ok {
if pollingSignalErr . IsStopTrying ( ) {
2024-11-04 20:10:45 +00:00
if pollingSignalErr . IsSuccessful ( ) {
if assertion . asyncType == AsyncAssertionTypeEventually {
fail ( "Told to stop trying (and ignoring call to Successfully(), as it is only relevant with Consistently)" )
} else {
return true // early escape hatch for Consistently
}
} else {
fail ( "Told to stop trying" )
}
2023-02-01 12:50:52 +00:00
return false
}
if pollingSignalErr . IsTryAgainAfter ( ) {
nextPoll = time . After ( pollingSignalErr . TryAgainDuration ( ) )
isTryAgainAfterError = true
}
2019-05-31 09:45:11 +00:00
}
}
2022-11-01 15:23:27 +00:00
2023-02-01 12:50:52 +00:00
if actualErr == nil && matcherErr == nil && matches == desiredMatch {
2022-11-01 15:23:27 +00:00
if assertion . asyncType == AsyncAssertionTypeEventually {
2023-01-30 20:05:01 +00:00
passedRepeatedlyCount += 1
if passedRepeatedlyCount == assertion . mustPassRepeatedly {
return true
}
2022-11-01 15:23:27 +00:00
}
} else if ! isTryAgainAfterError {
if assertion . asyncType == AsyncAssertionTypeConsistently {
2019-05-31 09:45:11 +00:00
fail ( "Failed" )
return false
}
2023-01-30 20:05:01 +00:00
// Reset the consecutive pass count
passedRepeatedlyCount = 0
2022-11-01 15:23:27 +00:00
}
2019-05-31 09:45:11 +00:00
2022-11-01 15:23:27 +00:00
if oracleMatcherSaysStop {
if assertion . asyncType == AsyncAssertionTypeEventually {
fail ( "No future change is possible. Bailing out early" )
return false
} else {
2019-05-31 09:45:11 +00:00
return true
}
2022-11-01 15:23:27 +00:00
}
2019-05-31 09:45:11 +00:00
2022-11-01 15:23:27 +00:00
if nextPoll == nil {
nextPoll = assertion . afterPolling ( )
}
select {
case <- nextPoll :
2023-02-01 12:50:52 +00:00
a , e := pollActual ( )
2022-11-01 15:23:27 +00:00
lock . Lock ( )
2023-02-01 12:50:52 +00:00
actual , actualErr = a , e
2022-11-01 15:23:27 +00:00
lock . Unlock ( )
2023-02-01 12:50:52 +00:00
if actualErr == nil {
lock . Lock ( )
lastValidActual = actual
hasLastValidActual = true
lock . Unlock ( )
oracleMatcherSaysStop = assertion . matcherSaysStopTrying ( matcher , actual )
m , e := assertion . pollMatcher ( matcher , actual )
2022-10-17 17:38:08 +00:00
lock . Lock ( )
2023-02-01 12:50:52 +00:00
matches , matcherErr = m , e
2022-10-17 17:38:08 +00:00
lock . Unlock ( )
2022-11-01 15:23:27 +00:00
}
case <- contextDone :
2024-01-29 20:22:47 +00:00
err := context . Cause ( assertion . ctx )
if err != nil && err != context . Canceled {
fail ( fmt . Sprintf ( "Context was cancelled (cause: %s)" , err ) )
} else {
fail ( "Context was cancelled" )
}
2022-11-01 15:23:27 +00:00
return false
case <- timeout :
if assertion . asyncType == AsyncAssertionTypeEventually {
fail ( "Timed out" )
2022-10-17 17:38:08 +00:00
return false
2022-11-01 15:23:27 +00:00
} else {
if isTryAgainAfterError {
fail ( "Timed out while waiting on TryAgainAfter" )
return false
}
2019-05-31 09:45:11 +00:00
return true
}
}
}
}