mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-18 20:30:23 +00:00
4a1591236d
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.1.4 to 2.1.6. - [Release notes](https://github.com/onsi/ginkgo/releases) - [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/ginkgo/compare/v2.1.4...v2.1.6) --- updated-dependencies: - dependency-name: github.com/onsi/ginkgo/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
238 lines
6.6 KiB
Go
238 lines
6.6 KiB
Go
package internal
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"runtime"
|
|
"time"
|
|
|
|
"github.com/onsi/gomega/types"
|
|
)
|
|
|
|
type AsyncAssertionType uint
|
|
|
|
const (
|
|
AsyncAssertionTypeEventually AsyncAssertionType = iota
|
|
AsyncAssertionTypeConsistently
|
|
)
|
|
|
|
type AsyncAssertion struct {
|
|
asyncType AsyncAssertionType
|
|
|
|
actualIsFunc bool
|
|
actualValue interface{}
|
|
actualFunc func() ([]reflect.Value, error)
|
|
|
|
timeoutInterval time.Duration
|
|
pollingInterval time.Duration
|
|
offset int
|
|
g *Gomega
|
|
}
|
|
|
|
func NewAsyncAssertion(asyncType AsyncAssertionType, actualInput interface{}, g *Gomega, timeoutInterval time.Duration, pollingInterval time.Duration, offset int) *AsyncAssertion {
|
|
out := &AsyncAssertion{
|
|
asyncType: asyncType,
|
|
timeoutInterval: timeoutInterval,
|
|
pollingInterval: pollingInterval,
|
|
offset: offset,
|
|
g: g,
|
|
}
|
|
|
|
switch actualType := reflect.TypeOf(actualInput); {
|
|
case actualInput == nil || actualType.Kind() != reflect.Func:
|
|
out.actualValue = actualInput
|
|
case actualType.NumIn() == 0 && actualType.NumOut() > 0:
|
|
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) 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...)
|
|
}
|
|
|
|
func (assertion *AsyncAssertion) buildDescription(optionalDescription ...interface{}) string {
|
|
switch len(optionalDescription) {
|
|
case 0:
|
|
return ""
|
|
case 1:
|
|
if describe, ok := optionalDescription[0].(func() string); ok {
|
|
return describe() + "\n"
|
|
}
|
|
}
|
|
return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n"
|
|
}
|
|
|
|
func (assertion *AsyncAssertion) pollActual() (interface{}, error) {
|
|
if !assertion.actualIsFunc {
|
|
return assertion.actualValue, nil
|
|
}
|
|
|
|
values, err := assertion.actualFunc()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
extras := []interface{}{nil}
|
|
for _, value := range values[1:] {
|
|
extras = append(extras, value.Interface())
|
|
}
|
|
success, message := vetActuals(extras, 0)
|
|
if !success {
|
|
return nil, errors.New(message)
|
|
}
|
|
|
|
return values[0].Interface(), nil
|
|
}
|
|
|
|
func (assertion *AsyncAssertion) matcherMayChange(matcher types.GomegaMatcher, value interface{}) bool {
|
|
if assertion.actualIsFunc {
|
|
return true
|
|
}
|
|
return types.MatchMayChangeInTheFuture(matcher, value)
|
|
}
|
|
|
|
func (assertion *AsyncAssertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
|
|
timer := time.Now()
|
|
timeout := time.After(assertion.timeoutInterval)
|
|
|
|
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)
|
|
}
|
|
|
|
assertion.g.THelper()
|
|
|
|
fail := func(preamble string) {
|
|
errMsg := ""
|
|
message := ""
|
|
if err != nil {
|
|
errMsg = "Error: " + err.Error()
|
|
} else {
|
|
if desiredMatch {
|
|
message = matcher.FailureMessage(value)
|
|
} else {
|
|
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)
|
|
}
|
|
|
|
if assertion.asyncType == AsyncAssertionTypeEventually {
|
|
for {
|
|
if err == nil && matches == desiredMatch {
|
|
return true
|
|
}
|
|
|
|
if !mayChange {
|
|
fail("No future change is possible. Bailing out early")
|
|
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
|
|
}
|
|
}
|
|
} else if assertion.asyncType == AsyncAssertionTypeConsistently {
|
|
for {
|
|
if !(err == nil && matches == desiredMatch) {
|
|
fail("Failed")
|
|
return false
|
|
}
|
|
|
|
if !mayChange {
|
|
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)
|
|
}
|
|
case <-timeout:
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|