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