github.com/onsi/gomega@v1.32.0/internal/assertion.go (about) 1 package internal 2 3 import ( 4 "fmt" 5 "reflect" 6 7 "github.com/onsi/gomega/format" 8 "github.com/onsi/gomega/types" 9 ) 10 11 type Assertion struct { 12 actuals []interface{} // actual value plus all extra values 13 actualIndex int // value to pass to the matcher 14 vet vetinari // the vet to call before calling Gomega matcher 15 offset int 16 g *Gomega 17 } 18 19 // ...obligatory discworld reference, as "vetineer" doesn't sound ... quite right. 20 type vetinari func(assertion *Assertion, optionalDescription ...interface{}) bool 21 22 func NewAssertion(actualInput interface{}, g *Gomega, offset int, extra ...interface{}) *Assertion { 23 return &Assertion{ 24 actuals: append([]interface{}{actualInput}, extra...), 25 actualIndex: 0, 26 vet: (*Assertion).vetActuals, 27 offset: offset, 28 g: g, 29 } 30 } 31 32 func (assertion *Assertion) WithOffset(offset int) types.Assertion { 33 assertion.offset = offset 34 return assertion 35 } 36 37 func (assertion *Assertion) Error() types.Assertion { 38 return &Assertion{ 39 actuals: assertion.actuals, 40 actualIndex: len(assertion.actuals) - 1, 41 vet: (*Assertion).vetError, 42 offset: assertion.offset, 43 g: assertion.g, 44 } 45 } 46 47 func (assertion *Assertion) Should(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { 48 assertion.g.THelper() 49 vetOptionalDescription("Assertion", optionalDescription...) 50 return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, true, optionalDescription...) 51 } 52 53 func (assertion *Assertion) ShouldNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { 54 assertion.g.THelper() 55 vetOptionalDescription("Assertion", optionalDescription...) 56 return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...) 57 } 58 59 func (assertion *Assertion) To(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { 60 assertion.g.THelper() 61 vetOptionalDescription("Assertion", optionalDescription...) 62 return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, true, optionalDescription...) 63 } 64 65 func (assertion *Assertion) ToNot(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { 66 assertion.g.THelper() 67 vetOptionalDescription("Assertion", optionalDescription...) 68 return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...) 69 } 70 71 func (assertion *Assertion) NotTo(matcher types.GomegaMatcher, optionalDescription ...interface{}) bool { 72 assertion.g.THelper() 73 vetOptionalDescription("Assertion", optionalDescription...) 74 return assertion.vet(assertion, optionalDescription...) && assertion.match(matcher, false, optionalDescription...) 75 } 76 77 func (assertion *Assertion) buildDescription(optionalDescription ...interface{}) string { 78 switch len(optionalDescription) { 79 case 0: 80 return "" 81 case 1: 82 if describe, ok := optionalDescription[0].(func() string); ok { 83 return describe() + "\n" 84 } 85 } 86 return fmt.Sprintf(optionalDescription[0].(string), optionalDescription[1:]...) + "\n" 87 } 88 89 func (assertion *Assertion) match(matcher types.GomegaMatcher, desiredMatch bool, optionalDescription ...interface{}) bool { 90 actualInput := assertion.actuals[assertion.actualIndex] 91 matches, err := matcher.Match(actualInput) 92 assertion.g.THelper() 93 if err != nil { 94 description := assertion.buildDescription(optionalDescription...) 95 assertion.g.Fail(description+err.Error(), 2+assertion.offset) 96 return false 97 } 98 if matches != desiredMatch { 99 var message string 100 if desiredMatch { 101 message = matcher.FailureMessage(actualInput) 102 } else { 103 message = matcher.NegatedFailureMessage(actualInput) 104 } 105 description := assertion.buildDescription(optionalDescription...) 106 assertion.g.Fail(description+message, 2+assertion.offset) 107 return false 108 } 109 110 return true 111 } 112 113 // vetActuals vets the actual values, with the (optional) exception of a 114 // specific value, such as the first value in case non-error assertions, or the 115 // last value in case of Error()-based assertions. 116 func (assertion *Assertion) vetActuals(optionalDescription ...interface{}) bool { 117 success, message := vetActuals(assertion.actuals, assertion.actualIndex) 118 if success { 119 return true 120 } 121 122 description := assertion.buildDescription(optionalDescription...) 123 assertion.g.THelper() 124 assertion.g.Fail(description+message, 2+assertion.offset) 125 return false 126 } 127 128 // vetError vets the actual values, except for the final error value, in case 129 // the final error value is non-zero. Otherwise, it doesn't vet the actual 130 // values, as these are allowed to take on any values unless there is a non-zero 131 // error value. 132 func (assertion *Assertion) vetError(optionalDescription ...interface{}) bool { 133 if err := assertion.actuals[assertion.actualIndex]; err != nil { 134 // Go error result idiom: all other actual values must be zero values. 135 return assertion.vetActuals(optionalDescription...) 136 } 137 return true 138 } 139 140 // vetActuals vets a slice of actual values, optionally skipping a particular 141 // value slice element, such as the first or last value slice element. 142 func vetActuals(actuals []interface{}, skipIndex int) (bool, string) { 143 for i, actual := range actuals { 144 if i == skipIndex { 145 continue 146 } 147 if actual != nil { 148 zeroValue := reflect.Zero(reflect.TypeOf(actual)).Interface() 149 if !reflect.DeepEqual(zeroValue, actual) { 150 var message string 151 if err, ok := actual.(error); ok { 152 message = fmt.Sprintf("Unexpected error: %s\n%s", err, format.Object(err, 1)) 153 } else { 154 message = fmt.Sprintf("Unexpected non-nil/non-zero argument at index %d:\n\t<%T>: %#v", i, actual, actual) 155 } 156 return false, message 157 } 158 } 159 } 160 return true, "" 161 }