2021-09-01 13:50:18 +00:00
|
|
|
package internal
|
2019-05-31 09:45:11 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
|
|
|
|
"github.com/onsi/gomega/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Assertion struct {
|
2021-11-08 20:26:59 +00:00
|
|
|
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
|
2019-05-31 09:45:11 +00:00
|
|
|
offset int
|
2021-09-01 13:50:18 +00:00
|
|
|
g *Gomega
|
2019-05-31 09:45:11 +00:00
|
|
|
}
|
|
|
|
|
2021-11-08 20:26:59 +00:00
|
|
|
// ...obligatory discworld reference, as "vetineer" doesn't sound ... quite right.
|
|
|
|
type vetinari func(assertion *Assertion, optionalDescription ...interface{}) bool
|
|
|
|
|
2021-09-01 13:50:18 +00:00
|
|
|
func NewAssertion(actualInput interface{}, g *Gomega, offset int, extra ...interface{}) *Assertion {
|
2019-05-31 09:45:11 +00:00
|
|
|
return &Assertion{
|
2021-11-08 20:26:59 +00:00
|
|
|
actuals: append([]interface{}{actualInput}, extra...),
|
|
|
|
actualIndex: 0,
|
|
|
|
vet: (*Assertion).vetActuals,
|
2019-05-31 09:45:11 +00:00
|
|
|
offset: offset,
|
2021-09-01 13:50:18 +00:00
|
|
|
g: g,
|
2019-05-31 09:45:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-08 20:26:59 +00:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-31 09:45:11 +00:00
|
|
|
func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
|
2021-09-01 13:50:18 +00:00
|
|
|
assertion.g.THelper()
|
2021-11-08 20:26:59 +00:00
|
|
|
return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
|
2019-05-31 09:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
|
2021-09-01 13:50:18 +00:00
|
|
|
assertion.g.THelper()
|
2021-11-08 20:26:59 +00:00
|
|
|
return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
|
2019-05-31 09:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
|
2021-09-01 13:50:18 +00:00
|
|
|
assertion.g.THelper()
|
2021-11-08 20:26:59 +00:00
|
|
|
return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, true, optionalDescription...)
|
2019-05-31 09:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
|
2021-09-01 13:50:18 +00:00
|
|
|
assertion.g.THelper()
|
2021-11-08 20:26:59 +00:00
|
|
|
return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
|
2019-05-31 09:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool {
|
2021-09-01 13:50:18 +00:00
|
|
|
assertion.g.THelper()
|
2021-11-08 20:26:59 +00:00
|
|
|
return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...)
|
2019-05-31 09:45:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (assertion *Assertion) 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
|
|
|
}
|
|
|
|
|
|
|
|
func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool {
|
2021-11-08 20:26:59 +00:00
|
|
|
actualInput := assertion.actuals[assertion.actualIndex]
|
|
|
|
matches, err := matcher.Match(actualInput)
|
2021-09-01 13:50:18 +00:00
|
|
|
assertion.g.THelper()
|
2019-05-31 09:45:11 +00:00
|
|
|
if err != nil {
|
2020-01-14 10:38:55 +00:00
|
|
|
description := assertion.buildDescription(optionalDescription...)
|
2021-09-01 13:50:18 +00:00
|
|
|
assertion.g.Fail(description+err.Error(), 2+assertion.offset)
|
2019-05-31 09:45:11 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
if matches != desiredMatch {
|
|
|
|
var message string
|
|
|
|
if desiredMatch {
|
2021-11-08 20:26:59 +00:00
|
|
|
message = matcher.FailureMessage(actualInput)
|
2019-05-31 09:45:11 +00:00
|
|
|
} else {
|
2021-11-08 20:26:59 +00:00
|
|
|
message = matcher.NegatedFailureMessage(actualInput)
|
2019-05-31 09:45:11 +00:00
|
|
|
}
|
2020-01-14 10:38:55 +00:00
|
|
|
description := assertion.buildDescription(optionalDescription...)
|
2021-09-01 13:50:18 +00:00
|
|
|
assertion.g.Fail(description+message, 2+assertion.offset)
|
2019-05-31 09:45:11 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-11-08 20:26:59 +00:00
|
|
|
// 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)
|
2019-05-31 09:45:11 +00:00
|
|
|
if success {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
description := assertion.buildDescription(optionalDescription...)
|
2021-09-01 13:50:18 +00:00
|
|
|
assertion.g.THelper()
|
|
|
|
assertion.g.Fail(description+message, 2+assertion.offset)
|
2019-05-31 09:45:11 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2021-11-08 20:26:59 +00:00
|
|
|
// 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) {
|
|
|
|
message := fmt.Sprintf("Unexpected non-nil/non-zero argument at index %d:\n\t<%T>: %#v", i, actual, actual)
|
2019-05-31 09:45:11 +00:00
|
|
|
return false, message
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true, ""
|
|
|
|
}
|