build(deps): bump github.com/onsi/gomega from 1.15.0 to 1.24.2

Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.15.0 to 1.24.2.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.15.0...v1.24.2)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Matthieu MOREL <matthieu.morel35@gmail.com>
This commit is contained in:
dependabot[bot]
2022-12-26 12:51:04 +01:00
committed by Matthieu MOREL
parent c5e81e3c05
commit 020b8db6ab
357 changed files with 53568 additions and 14989 deletions

View File

@ -4,48 +4,74 @@ import (
"fmt"
"reflect"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
)
type Assertion struct {
actualInput interface{}
actuals []interface{} // actual value plus all extra values
actualIndex int // value to pass to the matcher
vet vetinari // the vet to call before calling Gomega matcher
offset int
extra []interface{}
g *Gomega
}
// ...obligatory discworld reference, as "vetineer" doesn't sound ... quite right.
type vetinari func(assertion *Assertion, optionalDescription ...interface{}) bool
func NewAssertion(actualInput interface{}, g *Gomega, offset int, extra ...interface{}) *Assertion {
return &Assertion{
actualInput: actualInput,
actuals: append([]interface{}{actualInput}, extra...),
actualIndex: 0,
vet: (*Assertion).vetActuals,
offset: offset,
extra: extra,
g: g,
}
}
func (assertion *Assertion) WithOffset(offset int) types.Assertion {
assertion.offset = offset
return assertion
}
func (assertion *Assertion) Error() types.Assertion {
return &Assertion{
actuals: assertion.actuals,
actualIndex: len(assertion.actuals) - 1,
vet: (*Assertion).vetError,
offset: assertion.offset,
g: assertion.g,
}
}
func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
assertion.g.THelper()
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
vetOptionalDescription("Assertion", optionalDescription...)
return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
}
func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
assertion.g.THelper()
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
vetOptionalDescription("Assertion", optionalDescription...)
return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
}
func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
assertion.g.THelper()
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
vetOptionalDescription("Assertion", optionalDescription...)
return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
}
func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
assertion.g.THelper()
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
vetOptionalDescription("Assertion", optionalDescription...)
return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
}
func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
assertion.g.THelper()
return assertion.vetExtras(optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
vetOptionalDescription("Assertion", optionalDescription...)
return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
}
func (assertion *Assertion) buildDescription(optionalDescription ...interface{}) string {
@ -61,7 +87,8 @@ func (assertion *Assertion) buildDescription(optionalDescription ...interface{})
}
func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
matches, err := matcher.Match(assertion.actualInput)
actualInput := assertion.actuals[assertion.actualIndex]
matches, err := matcher.Match(actualInput)
assertion.g.THelper()
if err != nil {
description := assertion.buildDescription(optionalDescription...)
@ -71,9 +98,9 @@ func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool
if matches != desiredMatch {
var message string
if desiredMatch {
message = matcher.FailureMessage(assertion.actualInput)
message = matcher.FailureMessage(actualInput)
} else {
message = matcher.NegatedFailureMessage(assertion.actualInput)
message = matcher.NegatedFailureMessage(actualInput)
}
description := assertion.buildDescription(optionalDescription...)
assertion.g.Fail(description+message, 2+assertion.offset)
@ -83,8 +110,11 @@ func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool
return true
}
func (assertion *Assertion) vetExtras(optionalDescription ...interface{}) bool {
success, message := vetExtras(assertion.extra)
// vetActuals vets the actual values, with the (optional) exception of a
// specific value, such as the first value in case non-error assertions, or the
// last value in case of Error()-based assertions.
func (assertion *Assertion) vetActuals(optionalDescription ...interface{}) bool {
success, message := vetActuals(assertion.actuals, assertion.actualIndex)
if success {
return true
}
@ -95,12 +125,34 @@ func (assertion *Assertion) vetExtras(optionalDescription ...interface{}) bool {
return false
}
func vetExtras(extras []interface{}) (bool, string) {
for i, extra := range extras {
if extra != nil {
zeroValue := reflect.Zero(reflect.TypeOf(extra)).Interface()
if !reflect.DeepEqual(zeroValue, extra) {
message := fmt.Sprintf("Unexpected non-nil/non-zero extra argument at index %d:\n\t<%T>: %#v", i+1, extra, extra)
// vetError vets the actual values, except for the final error value, in case
// the final error value is non-zero. Otherwise, it doesn't vet the actual
// values, as these are allowed to take on any values unless there is a non-zero
// error value.
func (assertion *Assertion) vetError(optionalDescription ...interface{}) bool {
if err := assertion.actuals[assertion.actualIndex]; err != nil {
// Go error result idiom: all other actual values must be zero values.
return assertion.vetActuals(optionalDescription...)
}
return true
}
// vetActuals vets a slice of actual values, optionally skipping a particular
// value slice element, such as the first or last value slice element.
func vetActuals(actuals []interface{}, skipIndex int) (bool, string) {
for i, actual := range actuals {
if i == skipIndex {
continue
}
if actual != nil {
zeroValue := reflect.Zero(reflect.TypeOf(actual)).Interface()
if !reflect.DeepEqual(zeroValue, actual) {
var message string
if err, ok := actual.(error); ok {
message = fmt.Sprintf("Unexpected error: %s\n%s", err, format.Object(err, 1))
} else {
message = fmt.Sprintf("Unexpected non-nil/non-zero argument at index %d:\n\t<%T>: %#v", i, actual, actual)
}
return false, message
}
}

View File

@ -1,15 +1,36 @@
package internal
import (
"errors"
"context"
"fmt"
"reflect"
"runtime"
"sync"
"time"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/types"
)
var errInterface = reflect.TypeOf((*error)(nil)).Elem()
var gomegaType = reflect.TypeOf((*types.Gomega)(nil)).Elem()
var contextType = reflect.TypeOf(new(context.Context)).Elem()
type contextWithAttachProgressReporter interface {
AttachProgressReporter(func() string) func()
}
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.`
}
type AsyncAssertionType uint
const (
@ -17,83 +38,92 @@ const (
AsyncAssertionTypeConsistently
)
func (at AsyncAssertionType) String() string {
switch at {
case AsyncAssertionTypeEventually:
return "Eventually"
case AsyncAssertionTypeConsistently:
return "Consistently"
}
return "INVALID ASYNC ASSERTION TYPE"
}
type AsyncAssertion struct {
asyncType AsyncAssertionType
actualIsFunc bool
actualValue interface{}
actualFunc func() ([]reflect.Value, error)
actualIsFunc bool
actual interface{}
argsToForward []interface{}
timeoutInterval time.Duration
pollingInterval time.Duration
ctx context.Context
offset int
g *Gomega
}
func NewAsyncAssertion(asyncType AsyncAssertionType, actualInput interface{}, g *Gomega, timeoutInterval time.Duration, pollingInterval time.Duration, offset int) *AsyncAssertion {
func NewAsyncAssertion(asyncType AsyncAssertionType, actualInput interface{}, g *Gomega, timeoutInterval time.Duration, pollingInterval time.Duration, ctx context.Context, offset int) *AsyncAssertion {
out := &AsyncAssertion{
asyncType: asyncType,
timeoutInterval: timeoutInterval,
pollingInterval: pollingInterval,
offset: offset,
ctx: ctx,
g: g,
}
switch actualType := reflect.TypeOf(actualInput); {
case actualType.Kind() != reflect.Func:
out.actualValue = actualInput
case actualType.NumIn() == 0 && actualType.NumOut() > 0:
out.actual = actualInput
if actualInput != nil && reflect.TypeOf(actualInput).Kind() == reflect.Func {
out.actualIsFunc = true
out.actualFunc = func() ([]reflect.Value, error) {
return reflect.ValueOf(actualInput).Call([]reflect.Value{}), nil
}
case actualType.NumIn() == 1 && actualType.In(0).Implements(reflect.TypeOf((*types.Gomega)(nil)).Elem()):
out.actualIsFunc = true
out.actualFunc = func() (values []reflect.Value, err error) {
var assertionFailure error
assertionCapturingGomega := NewGomega(g.DurationBundle).ConfigureWithFailHandler(func(message string, callerSkip ...int) {
skip := 0
if len(callerSkip) > 0 {
skip = callerSkip[0]
}
_, file, line, _ := runtime.Caller(skip + 1)
assertionFailure = fmt.Errorf("Assertion in callback at %s:%d failed:\n%s", file, line, message)
panic("stop execution")
})
defer func() {
if actualType.NumOut() == 0 {
if assertionFailure == nil {
values = []reflect.Value{reflect.Zero(reflect.TypeOf((*error)(nil)).Elem())}
} else {
values = []reflect.Value{reflect.ValueOf(assertionFailure)}
}
} else {
err = assertionFailure
}
if e := recover(); e != nil && assertionFailure == nil {
panic(e)
}
}()
values = reflect.ValueOf(actualInput).Call([]reflect.Value{reflect.ValueOf(assertionCapturingGomega)})
return
}
default:
msg := fmt.Sprintf("The function passed to Gomega's async assertions should either take no arguments and return values, or take a single Gomega interface that it can use to make assertions within the body of the function. When taking a Gomega interface the function can optionally return values or return nothing. The function you passed takes %d arguments and returns %d values.", actualType.NumIn(), actualType.NumOut())
g.Fail(msg, offset+4)
}
return out
}
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
}
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
}
func (assertion *AsyncAssertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
assertion.g.THelper()
vetOptionalDescription("Asynchronous assertion", optionalDescription...)
return assertion.match(matcher, true, optionalDescription...)
}
func (assertion *AsyncAssertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
assertion.g.THelper()
vetOptionalDescription("Asynchronous assertion", optionalDescription...)
return assertion.match(matcher, false, optionalDescription...)
}
@ -109,54 +139,238 @@ func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interfa
return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n"
}
func (assertion *AsyncAssertion) pollActual() (interface{}, error) {
if !assertion.actualIsFunc {
return assertion.actualValue, nil
func (assertion *AsyncAssertion) processReturnValues(values []reflect.Value) (interface{}, error) {
if len(values) == 0 {
return nil, fmt.Errorf("No values were returned by the function passed to Gomega")
}
values, err := assertion.actualFunc()
if err != nil {
return nil, err
}
extras := []interface{}{}
for _, value := range values[1:] {
extras = append(extras, value.Interface())
}
success, message := vetExtras(extras)
if !success {
return nil, errors.New(message)
actual := values[0].Interface()
if _, ok := AsPollingSignalError(actual); ok {
return actual, actual.(error)
}
return values[0].Interface(), nil
var err error
for i, extraValue := range values[1:] {
extra := extraValue.Interface()
if extra == nil {
continue
}
if _, ok := AsPollingSignalError(extra); ok {
return actual, extra.(error)
}
extraType := reflect.TypeOf(extra)
zero := reflect.Zero(extraType).Interface()
if reflect.DeepEqual(extra, zero) {
continue
}
if i == len(values)-2 && extraType.Implements(errInterface) {
err = fmt.Errorf("function returned error: %w", extra.(error))
}
if err == nil {
err = fmt.Errorf("Unexpected non-nil/non-zero return value at index %d:\n\t<%T>: %#v", i+1, extra, extra)
}
}
return actual, err
}
func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool {
if assertion.actualIsFunc {
return true
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 types.MatchMayChangeInTheFuture(matcher, value)
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)
}
func (assertion *AsyncAssertion) buildActualPoller() (func() (interface{}, error), error) {
if !assertion.actualIsFunc {
return func() (interface{}, error) { return assertion.actual, nil }, nil
}
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)
}
takesGomega, takesContext := false, false
if numIn > 0 {
takesGomega, takesContext = actualType.In(0).Implements(gomegaType), actualType.In(0).Implements(contextType)
}
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)
assertionFailure = fmt.Errorf("Assertion in callback at %s:%d failed:\n%s", file, line, message)
// we throw an asyncGomegaHaltExecutionError so that defer GinkgoRecover() can catch this error if the user makes an assertion in a goroutine
panic(asyncGomegaHaltExecutionError{})
})))
}
if takesContext {
inValues = append(inValues, reflect.ValueOf(assertion.ctx))
}
for _, arg := range assertion.argsToForward {
inValues = append(inValues, reflect.ValueOf(arg))
}
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))
}
return func() (actual interface{}, err error) {
var values []reflect.Value
assertionFailure = nil
defer func() {
if numOut == 0 && takesGomega {
actual = assertionFailure
} else {
actual, err = assertion.processReturnValues(values)
_, isAsyncError := AsPollingSignalError(err)
if assertionFailure != nil && !isAsyncError {
err = assertionFailure
}
}
if e := recover(); e != nil {
if _, isAsyncError := AsPollingSignalError(e); isAsyncError {
err = e.(error)
} else if assertionFailure == nil {
panic(e)
}
}
}()
values = actualValue.Call(inValues)
return
}, nil
}
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 {
if assertion.ctx == nil {
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)
}
}
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
}
func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
timer := time.Now()
timeout := time.After(assertion.timeoutInterval)
timeout := assertion.afterTimeout()
lock := sync.Mutex{}
var matches bool
var err error
mayChange := true
value, err := assertion.pollActual()
if err == nil {
mayChange = assertion.matcherMayChange(matcher, value)
matches, err = matcher.Match(value)
}
var oracleMatcherSaysStop bool
assertion.g.THelper()
fail := func(preamble string) {
errMsg := ""
pollActual, err := assertion.buildActualPoller()
if err != nil {
assertion.g.Fail(err.Error(), 2+assertion.offset)
return false
}
value, err := pollActual()
if err == nil {
oracleMatcherSaysStop = assertion.matcherSaysStopTrying(matcher, value)
matches, err = assertion.pollMatcher(matcher, value)
}
messageGenerator := func() string {
// can be called out of band by Ginkgo if the user requests a progress report
lock.Lock()
defer lock.Unlock()
message := ""
if err != nil {
errMsg = "Error: " + err.Error()
if pollingSignalErr, ok := AsPollingSignalError(err); ok && pollingSignalErr.IsStopTrying() {
message = err.Error()
for _, attachment := range pollingSignalErr.Attachments {
message += fmt.Sprintf("\n%s:\n", attachment.Description)
message += format.Object(attachment.Object, 1)
}
} else {
message = "Error: " + err.Error() + "\n" + format.Object(err, 1)
}
} else {
if desiredMatch {
message = matcher.FailureMessage(value)
@ -164,57 +378,90 @@ func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch
message = matcher.NegatedFailureMessage(value)
}
}
assertion.g.THelper()
description := assertion.buildDescription(optionalDescription...)
assertion.g.Fail(fmt.Sprintf("%s after %.3fs.\n%s%s%s", preamble, time.Since(timer).Seconds(), description, message, errMsg), 3+assertion.offset)
return fmt.Sprintf("%s%s", description, message)
}
if assertion.asyncType == AsyncAssertionTypeEventually {
for {
if err == nil && matches == desiredMatch {
return true
}
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)
}
if !mayChange {
fail("No future change is possible. Bailing out early")
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()
}
}
for {
var nextPoll <-chan time.Time = nil
var isTryAgainAfterError = false
if pollingSignalErr, ok := AsPollingSignalError(err); ok {
if pollingSignalErr.IsStopTrying() {
fail("Told to stop trying")
return false
}
select {
case <-time.After(assertion.pollingInterval):
value, err = assertion.pollActual()
if err == nil {
mayChange = assertion.matcherMayChange(matcher, value)
matches, err = matcher.Match(value)
}
case <-timeout:
fail("Timed out")
return false
if pollingSignalErr.IsTryAgainAfter() {
nextPoll = time.After(pollingSignalErr.TryAgainDuration())
isTryAgainAfterError = true
}
}
} else if assertion.asyncType == AsyncAssertionTypeConsistently {
for {
if !(err == nil && matches == desiredMatch) {
if err == nil && matches == desiredMatch {
if assertion.asyncType == AsyncAssertionTypeEventually {
return true
}
} else if !isTryAgainAfterError {
if assertion.asyncType == AsyncAssertionTypeConsistently {
fail("Failed")
return false
}
}
if !mayChange {
if oracleMatcherSaysStop {
if assertion.asyncType == AsyncAssertionTypeEventually {
fail("No future change is possible. Bailing out early")
return false
} else {
return true
}
}
select {
case <-time.After(assertion.pollingInterval):
value, err = assertion.pollActual()
if err == nil {
mayChange = assertion.matcherMayChange(matcher, value)
matches, err = matcher.Match(value)
if nextPoll == nil {
nextPoll = assertion.afterPolling()
}
select {
case <-nextPoll:
v, e := pollActual()
lock.Lock()
value, err = v, e
lock.Unlock()
if err == nil {
oracleMatcherSaysStop = assertion.matcherSaysStopTrying(matcher, value)
m, e := assertion.pollMatcher(matcher, value)
lock.Lock()
matches, err = m, e
lock.Unlock()
}
case <-contextDone:
fail("Context was cancelled")
return false
case <-timeout:
if assertion.asyncType == AsyncAssertionTypeEventually {
fail("Timed out")
return false
} else {
if isTryAgainAfterError {
fail("Timed out while waiting on TryAgainAfter")
return false
}
case <-timeout:
return true
}
}
}
return false
}

View File

@ -44,28 +44,28 @@ func durationFromEnv(key string, defaultDuration time.Duration) time.Duration {
return duration
}
func toDuration(input interface{}) time.Duration {
func toDuration(input interface{}) (time.Duration, error) {
duration, ok := input.(time.Duration)
if ok {
return duration
return duration, nil
}
value := reflect.ValueOf(input)
kind := reflect.TypeOf(input).Kind()
if reflect.Int <= kind && kind <= reflect.Int64 {
return time.Duration(value.Int()) * time.Second
return time.Duration(value.Int()) * time.Second, nil
} else if reflect.Uint <= kind && kind <= reflect.Uint64 {
return time.Duration(value.Uint()) * time.Second
return time.Duration(value.Uint()) * time.Second, nil
} else if reflect.Float32 <= kind && kind <= reflect.Float64 {
return time.Duration(value.Float() * float64(time.Second))
return time.Duration(value.Float() * float64(time.Second)), nil
} else if reflect.String == kind {
duration, err := time.ParseDuration(value.String())
if err != nil {
panic(fmt.Sprintf("%#v is not a valid parsable duration string.", input))
return 0, fmt.Errorf("%#v is not a valid parsable duration string: %w", input, err)
}
return duration
return duration, nil
}
panic(fmt.Sprintf("%v is not a valid interval. Must be time.Duration, parsable duration string or a number.", input))
return 0, fmt.Errorf("%#v is not a valid interval. Must be a time.Duration, a parsable duration string, or a number.", input)
}

View File

@ -1,6 +1,7 @@
package internal
import (
"context"
"time"
"github.com/onsi/gomega/types"
@ -39,50 +40,76 @@ func (g *Gomega) ConfigureWithT(t types.GomegaTestingT) *Gomega {
return g
}
func (g *Gomega) Ω(atual interface{}, extra ...interface{}) types.Assertion {
return g.ExpectWithOffset(0, atual, extra...)
func (g *Gomega) Ω(actual interface{}, extra ...interface{}) types.Assertion {
return g.ExpectWithOffset(0, actual, extra...)
}
func (g *Gomega) Expect(atual interface{}, extra ...interface{}) types.Assertion {
return g.ExpectWithOffset(0, atual, extra...)
func (g *Gomega) Expect(actual interface{}, extra ...interface{}) types.Assertion {
return g.ExpectWithOffset(0, actual, extra...)
}
func (g *Gomega) ExpectWithOffset(offset int, actual interface{}, extra ...interface{}) types.Assertion {
return NewAssertion(actual, g, offset, extra...)
}
func (g *Gomega) Eventually(actual interface{}, intervals ...interface{}) types.AsyncAssertion {
return g.EventuallyWithOffset(0, actual, intervals...)
func (g *Gomega) Eventually(actualOrCtx interface{}, args ...interface{}) types.AsyncAssertion {
return g.makeAsyncAssertion(AsyncAssertionTypeEventually, 0, actualOrCtx, args...)
}
func (g *Gomega) EventuallyWithOffset(offset int, actual interface{}, intervals ...interface{}) types.AsyncAssertion {
timeoutInterval := g.DurationBundle.EventuallyTimeout
pollingInterval := g.DurationBundle.EventuallyPollingInterval
func (g *Gomega) EventuallyWithOffset(offset int, actualOrCtx interface{}, args ...interface{}) types.AsyncAssertion {
return g.makeAsyncAssertion(AsyncAssertionTypeEventually, offset, actualOrCtx, args...)
}
func (g *Gomega) Consistently(actualOrCtx interface{}, args ...interface{}) types.AsyncAssertion {
return g.makeAsyncAssertion(AsyncAssertionTypeConsistently, 0, actualOrCtx, args...)
}
func (g *Gomega) ConsistentlyWithOffset(offset int, actualOrCtx interface{}, args ...interface{}) types.AsyncAssertion {
return g.makeAsyncAssertion(AsyncAssertionTypeConsistently, offset, actualOrCtx, args...)
}
func (g *Gomega) makeAsyncAssertion(asyncAssertionType AsyncAssertionType, offset int, actualOrCtx interface{}, args ...interface{}) types.AsyncAssertion {
baseOffset := 3
timeoutInterval := -time.Duration(1)
pollingInterval := -time.Duration(1)
intervals := []interface{}{}
var ctx context.Context
actual := actualOrCtx
startingIndex := 0
if _, isCtx := actualOrCtx.(context.Context); isCtx && len(args) > 0 {
// the first argument is a context, we should accept it as the context _only if_ it is **not** the only argumnent **and** the second argument is not a parseable duration
// this is due to an unfortunate ambiguity in early version of Gomega in which multi-type durations are allowed after the actual
if _, err := toDuration(args[0]); err != nil {
ctx = actualOrCtx.(context.Context)
actual = args[0]
startingIndex = 1
}
}
for _, arg := range args[startingIndex:] {
switch v := arg.(type) {
case context.Context:
ctx = v
default:
intervals = append(intervals, arg)
}
}
var err error
if len(intervals) > 0 {
timeoutInterval = toDuration(intervals[0])
timeoutInterval, err = toDuration(intervals[0])
if err != nil {
g.Fail(err.Error(), offset+baseOffset)
}
}
if len(intervals) > 1 {
pollingInterval = toDuration(intervals[1])
pollingInterval, err = toDuration(intervals[1])
if err != nil {
g.Fail(err.Error(), offset+baseOffset)
}
}
return NewAsyncAssertion(AsyncAssertionTypeEventually, actual, g, timeoutInterval, pollingInterval, offset)
}
func (g *Gomega) Consistently(actual interface{}, intervals ...interface{}) types.AsyncAssertion {
return g.ConsistentlyWithOffset(0, actual, intervals...)
}
func (g *Gomega) ConsistentlyWithOffset(offset int, actual interface{}, intervals ...interface{}) types.AsyncAssertion {
timeoutInterval := g.DurationBundle.ConsistentlyDuration
pollingInterval := g.DurationBundle.ConsistentlyPollingInterval
if len(intervals) > 0 {
timeoutInterval = toDuration(intervals[0])
}
if len(intervals) > 1 {
pollingInterval = toDuration(intervals[1])
}
return NewAsyncAssertion(AsyncAssertionTypeConsistently, actual, g, timeoutInterval, pollingInterval, offset)
return NewAsyncAssertion(asyncAssertionType, actual, g, timeoutInterval, pollingInterval, ctx, offset)
}
func (g *Gomega) SetDefaultEventuallyTimeout(t time.Duration) {

View File

@ -0,0 +1,48 @@
//go:build go1.16
// +build go1.16
// Package gutil is a replacement for ioutil, which should not be used in new
// code as of Go 1.16. With Go 1.16 and higher, this implementation
// uses the ioutil replacement functions in "io" and "os" with some
// Gomega specifics. This means that we should not get deprecation warnings
// for ioutil when they are added.
package gutil
import (
"io"
"os"
)
func NopCloser(r io.Reader) io.ReadCloser {
return io.NopCloser(r)
}
func ReadAll(r io.Reader) ([]byte, error) {
return io.ReadAll(r)
}
func ReadDir(dirname string) ([]string, error) {
entries, err := os.ReadDir(dirname)
if err != nil {
return nil, err
}
var names []string
for _, entry := range entries {
names = append(names, entry.Name())
}
return names, nil
}
func ReadFile(filename string) ([]byte, error) {
return os.ReadFile(filename)
}
func MkdirTemp(dir, pattern string) (string, error) {
return os.MkdirTemp(dir, pattern)
}
func WriteFile(filename string, data []byte) error {
return os.WriteFile(filename, data, 0644)
}

View File

@ -0,0 +1,47 @@
//go:build !go1.16
// +build !go1.16
// Package gutil is a replacement for ioutil, which should not be used in new
// code as of Go 1.16. With Go 1.15 and lower, this implementation
// uses the ioutil functions, meaning that although Gomega is not officially
// supported on these versions, it is still likely to work.
package gutil
import (
"io"
"io/ioutil"
)
func NopCloser(r io.Reader) io.ReadCloser {
return ioutil.NopCloser(r)
}
func ReadAll(r io.Reader) ([]byte, error) {
return ioutil.ReadAll(r)
}
func ReadDir(dirname string) ([]string, error) {
files, err := ioutil.ReadDir(dirname)
if err != nil {
return nil, err
}
var names []string
for _, file := range files {
names = append(names, file.Name())
}
return names, nil
}
func ReadFile(filename string) ([]byte, error) {
return ioutil.ReadFile(filename)
}
func MkdirTemp(dir, pattern string) (string, error) {
return ioutil.TempDir(dir, pattern)
}
func WriteFile(filename string, data []byte) error {
return ioutil.WriteFile(filename, data, 0644)
}

View File

@ -0,0 +1,106 @@
package internal
import (
"errors"
"fmt"
"time"
)
type PollingSignalErrorType int
const (
PollingSignalErrorTypeStopTrying PollingSignalErrorType = iota
PollingSignalErrorTypeTryAgainAfter
)
type PollingSignalError interface {
error
Wrap(err error) PollingSignalError
Attach(description string, obj any) PollingSignalError
Now()
}
var StopTrying = func(message string) PollingSignalError {
return &PollingSignalErrorImpl{
message: message,
pollingSignalErrorType: PollingSignalErrorTypeStopTrying,
}
}
var TryAgainAfter = func(duration time.Duration) PollingSignalError {
return &PollingSignalErrorImpl{
message: fmt.Sprintf("told to try again after %s", duration),
duration: duration,
pollingSignalErrorType: PollingSignalErrorTypeTryAgainAfter,
}
}
type PollingSignalErrorAttachment struct {
Description string
Object any
}
type PollingSignalErrorImpl struct {
message string
wrappedErr error
pollingSignalErrorType PollingSignalErrorType
duration time.Duration
Attachments []PollingSignalErrorAttachment
}
func (s *PollingSignalErrorImpl) Wrap(err error) PollingSignalError {
s.wrappedErr = err
return s
}
func (s *PollingSignalErrorImpl) Attach(description string, obj any) PollingSignalError {
s.Attachments = append(s.Attachments, PollingSignalErrorAttachment{description, obj})
return s
}
func (s *PollingSignalErrorImpl) Error() string {
if s.wrappedErr == nil {
return s.message
} else {
return s.message + ": " + s.wrappedErr.Error()
}
}
func (s *PollingSignalErrorImpl) Unwrap() error {
if s == nil {
return nil
}
return s.wrappedErr
}
func (s *PollingSignalErrorImpl) Now() {
panic(s)
}
func (s *PollingSignalErrorImpl) IsStopTrying() bool {
return s.pollingSignalErrorType == PollingSignalErrorTypeStopTrying
}
func (s *PollingSignalErrorImpl) IsTryAgainAfter() bool {
return s.pollingSignalErrorType == PollingSignalErrorTypeTryAgainAfter
}
func (s *PollingSignalErrorImpl) TryAgainDuration() time.Duration {
return s.duration
}
func AsPollingSignalError(actual interface{}) (*PollingSignalErrorImpl, bool) {
if actual == nil {
return nil, false
}
if actualErr, ok := actual.(error); ok {
var target *PollingSignalErrorImpl
if errors.As(actualErr, &target) {
return target, true
} else {
return nil, false
}
}
return nil, false
}

22
vendor/github.com/onsi/gomega/internal/vetoptdesc.go generated vendored Normal file
View File

@ -0,0 +1,22 @@
package internal
import (
"fmt"
"github.com/onsi/gomega/types"
)
// vetOptionalDescription vets the optional description args: if it finds any
// Gomega matcher at the beginning it panics. This allows for rendering Gomega
// matchers as part of an optional Description, as long as they're not in the
// first slot.
func vetOptionalDescription(assertion string, optionalDescription ...interface{}) {
if len(optionalDescription) == 0 {
return
}
if _, isGomegaMatcher := optionalDescription[0].(types.GomegaMatcher); isGomegaMatcher {
panic(fmt.Sprintf("%s has a GomegaMatcher as the first element of optionalDescription.\n\t"+
"Do you mean to use And/Or/SatisfyAll/SatisfyAny to combine multiple matchers?",
assertion))
}
}