Path: blob/main/vendor/github.com/onsi/gomega/internal/assertion.go
2880 views
package internal12import (3"fmt"4"reflect"56"github.com/onsi/gomega/format"7"github.com/onsi/gomega/types"8)910type Assertion struct {11actuals []any // actual value plus all extra values12actualIndex int // value to pass to the matcher13vet vetinari // the vet to call before calling Gomega matcher14offset int15g *Gomega16}1718// ...obligatory discworld reference, as "vetineer" doesn't sound ... quite right.19type vetinari func(assertion *Assertion, optionalDescription ...any) bool2021func NewAssertion(actualInput any, g *Gomega, offset int, extra ...any) *Assertion {22return &Assertion{23actuals: append([]any{actualInput}, extra...),24actualIndex: 0,25vet: (*Assertion).vetActuals,26offset: offset,27g: g,28}29}3031func (assertion *Assertion) WithOffset(offset int) types.Assertion {32assertion.offset = offset33return assertion34}3536func (assertion *Assertion) Error() types.Assertion {37return &Assertion{38actuals: assertion.actuals,39actualIndex: len(assertion.actuals) - 1,40vet: (*Assertion).vetError,41offset: assertion.offset,42g: assertion.g,43}44}4546func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...any) bool {47assertion.g.THelper()48vetOptionalDescription("Assertion", optionalDescription...)49return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, true, optionalDescription...)50}5152func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...any) bool {53assertion.g.THelper()54vetOptionalDescription("Assertion", optionalDescription...)55return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...)56}5758func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...any) bool {59assertion.g.THelper()60vetOptionalDescription("Assertion", optionalDescription...)61return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, true, optionalDescription...)62}6364func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...any) bool {65assertion.g.THelper()66vetOptionalDescription("Assertion", optionalDescription...)67return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...)68}6970func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...any) bool {71assertion.g.THelper()72vetOptionalDescription("Assertion", optionalDescription...)73return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...)74}7576func (assertion *Assertion) buildDescription(optionalDescription ...any) string {77switch len(optionalDescription) {78case 0:79return ""80case 1:81if describe, ok := optionalDescription[0].(func() string); ok {82return describe() + "\n"83}84}85return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n"86}8788func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...any) bool {89actualInput := assertion.actuals[assertion.actualIndex]90matches, err := matcher.Match(actualInput)91assertion.g.THelper()92if err != nil {93description := assertion.buildDescription(optionalDescription...)94assertion.g.Fail(description+err.Error(), 2+assertion.offset)95return false96}97if matches != desiredMatch {98var message string99if desiredMatch {100message = matcher.FailureMessage(actualInput)101} else {102message = matcher.NegatedFailureMessage(actualInput)103}104description := assertion.buildDescription(optionalDescription...)105assertion.g.Fail(description+message, 2+assertion.offset)106return false107}108109return true110}111112// vetActuals vets the actual values, with the (optional) exception of a113// specific value, such as the first value in case non-error assertions, or the114// last value in case of Error()-based assertions.115func (assertion *Assertion) vetActuals(optionalDescription ...any) bool {116success, message := vetActuals(assertion.actuals, assertion.actualIndex)117if success {118return true119}120121description := assertion.buildDescription(optionalDescription...)122assertion.g.THelper()123assertion.g.Fail(description+message, 2+assertion.offset)124return false125}126127// vetError vets the actual values, except for the final error value, in case128// the final error value is non-zero. Otherwise, it doesn't vet the actual129// values, as these are allowed to take on any values unless there is a non-zero130// error value.131func (assertion *Assertion) vetError(optionalDescription ...any) bool {132if err := assertion.actuals[assertion.actualIndex]; err != nil {133// Go error result idiom: all other actual values must be zero values.134return assertion.vetActuals(optionalDescription...)135}136return true137}138139// vetActuals vets a slice of actual values, optionally skipping a particular140// value slice element, such as the first or last value slice element.141func vetActuals(actuals []any, skipIndex int) (bool, string) {142for i, actual := range actuals {143if i == skipIndex {144continue145}146if actual != nil {147zeroValue := reflect.Zero(reflect.TypeOf(actual)).Interface()148if !reflect.DeepEqual(zeroValue, actual) {149var message string150if err, ok := actual.(error); ok {151message = fmt.Sprintf("Unexpected error: %s\n%s", err, format.Object(err, 1))152} else {153message = fmt.Sprintf("Unexpected non-nil/non-zero argument at index %d:\n\t<%T>: %#v", i, actual, actual)154}155return false, message156}157}158}159return true, ""160}161162163