// Constructs an if-and-only-if/then matcher: // matcher := IfAndOnlyIf(AntecedentMatcher).Then(ConsequentMatcher) // that matches when both or neither of the Antecedent and the // Consequent match. Note that this is logically equivalent to: // Either(Not(AntecedentMatcher)).Xor(ConsequentMatcher) // But may be more readable in practice. func (self *IfAndOnlyIfClause) Then(consequent *base.Matcher) *base.Matcher { antecedent := self.antecedent match := func(actual interface{}) *base.Result { result1 := antecedent.Match(actual) result2 := consequent.Match(actual) if result1.Matched() { if result2.Matched() { return base.NewResultf(true, "Matched because both parts of 'Iff/Then' matched on [%v]", actual). WithCauses(result1, result2) } return base.NewResultf(false, "Failed because only the first part of 'Iff/Then' matched on [%v]", actual). WithCauses(result1, result2) } if result2.Matched() { return base.NewResultf(false, "Failed because only the second part of 'IFf/Then' matched on [%v]", actual). WithCauses(result1, result2) } return base.NewResultf(true, "Matched because neither part of 'Iff/Then' matched on [%v]", actual). WithCauses(result1, result2) } return base.NewMatcherf(match, "if and only if [%v] then [%v]", antecedent, consequent) }
// Returns a matcher that matches on any array or slice input value // if the given matcher matches every element of that array or slice. // // The returned matcher does not match any non-array-or-slice value. func EachElem(matcher *base.Matcher) *base.Matcher { match := func(actual interface{}) *base.Result { v := reflect.NewValue(actual) var value _ElemAndLen var ok bool value, ok = v.(*reflect.ArrayValue) if !ok { value, ok = v.(*reflect.SliceValue) } if !ok { return base.NewResultf(false, "Was not array or slice: was type %T", actual) } n := value.Len() for i := 0; i < n; i++ { elem := value.Elem(i).Interface() result := matcher.Match(elem) if !result.Matched() { return base.NewResultf(false, "Failed to match element %v of %v: %v", i+1, n, elem). WithCauses(result) } } return base.NewResultf(true, "Matched all of the %v elements", n) } return base.NewMatcherf(match, "EveryElement[%v]", matcher) }
// Second part of a builder for an either/xor matcher: // matcher := Either(matcher1).Xor(matcher2) // This matcher matches when exactly one of the two matchers matches // a given value; if both or neither of the matchers is successful, // xor fails to match. Note that this is *never* a short-circuiting // operation. func (self *EitherClause) Xor(matcher2 *base.Matcher) *base.Matcher { matcher1 := self.matcher match := func(actual interface{}) *base.Result { result1 := matcher1.Match(actual) result2 := matcher2.Match(actual) if result1.Matched() { if result2.Matched() { return base.NewResultf(false, "both parts of 'Either/Xor' matched [%v]", actual). WithCauses(result1, result2) } return base.NewResultf(true, "only the first part of 'Either/Xor' matched [%v]", actual). WithCauses(result1, result2) } if result2.Matched() { return base.NewResultf(true, "only the second part of 'Either/Xor' matched [%v]", actual). WithCauses(result1, result2) } return base.NewResultf(false, "neither part of 'Either/Xor' matched [%v]", actual). WithCauses(result1, result2) } return base.NewMatcherf(match, "either [%v] xor [%v]", matcher1, matcher2) }
func logSamples(t *testing.T, matcher *base.Matcher) { t.Logf("Sample results for: %v\n", matcher) we := asserter.Using(t) for index, value := range sampleValues { t.Logf("Sample #%v: %T[value: %v]\n", index+1, value, value) we.LogResult(matcher.Match(value)) } }
// Creates a new matcher that applies the given matcher to the result of // converting an input string to uppercase (using strings.ToUpper). // If the input value is not a string, the matcher fails to match. func ToUpper(matcher *base.Matcher) *base.Matcher { match := func(s string) *base.Result { upper := strings.ToUpper(s) result := matcher.Match(upper) return base.NewResultf(result.Matched(), "ToUpper is %v", upper). WithCauses(result) } return base.NewMatcherf(match, "ToUpper(%v)", matcher) }
// Creates a new matcher that applies the given matcher to the result of // converting an input string its length. (using the `len()` builtin). // If the input value is not a string, the matcher fails to match. func ToLen(matcher *base.Matcher) *base.Matcher { match := func(s string) *base.Result { length := len(s) result := matcher.Match(length) return base.NewResultf(result.Matched(), "length is %v", length). WithCauses(result) } return base.NewMatcherf(match, "ToLen(%v)", matcher) }
// Returns a new matcher that applies the type of its input // element to the given matcher. func ToType(matcher *base.Matcher) *base.Matcher { match := func(actual interface{}) *base.Result { actualType := reflect.Typeof(actual) result := matcher.Match(actualType) return base.NewResultf(result.Matched(), "reflect.Typeof() returned %v", actualType). WithCauses(result) } return base.NewMatcherf(match, "ToType(%v)", matcher) }
func safeMatch(value interface{}, matcher *base.Matcher) (result *base.Result) { defer func() { if x := recover(); x != nil { result = base.NewResultf(false, "Panic: %v", x). WithMatcherAndValue(matcher, value) } }() result = matcher.Match(value) return }
// Applies the given matcher to the length of the input element. func ToLen(matcher *base.Matcher) *base.Matcher { match := func(actual interface{}) *base.Result { value := reflect.NewValue(actual) if hasLen, ok := value.(_HasLen); ok { length := hasLen.Len() result := matcher.Match(length) return base.NewResultf(result.Matched(), "Len() returned %v", length) } return base.NewResultf(false, "Can't determine Len() for %T", actual) } return base.NewMatcherf(match, "ToLen[%v]", matcher) }
// Returns a new matcher that, on any input that is a *reflect.SliceType, // extracts the type of element and matches it against the given matcher. // // If the given input is not an *reflect.SliceType, this fails to match. // Note: this matches slice *types*, not slices. (See SliceOf.) func SliceTypeOf(elementTypeMatcher *base.Matcher) *base.Matcher { match := func(actual interface{}) *base.Result { if sliceType, ok := actual.(*reflect.SliceType); ok { elementType := sliceType.Elem() result := elementTypeMatcher.Match(elementType) return base.NewResultf( result.Matched(), "was SliceType with elements of type %v", elementType.Name()). WithCauses(result) } return base.NewResultf(false, "was of type %T, not a slice", actual) } return base.NewMatcherf(match, "SliceTypeOf(%v)", elementTypeMatcher) }
// Returns a new matcher that, on any input that is an array, extracts // its type and matches it against the given matcher. // // If the given input is not an array, this fails to match. // Note: this matches *arrays*, not array *types*. (See ArrayTypeOf.) func ArrayOf(elementTypeMatcher *base.Matcher) *base.Matcher { match := func(actual interface{}) *base.Result { actualType := reflect.Typeof(actual) if arrayType, ok := actualType.(*reflect.ArrayType); ok { elementType := arrayType.Elem() result := elementTypeMatcher.Match(elementType) return base.NewResultf( result.Matched(), "was array with elements of type %v", elementType). WithCauses(result) } return base.NewResultf(false, "was of type %T, not an array", actual) } return base.NewMatcherf(match, "ArrayOf(%v)", elementTypeMatcher) }
// Returns a new matcher that, on any input that is a channel, extracts // its type and matches it against the given matcher. // // If the given input is not a channel, this fails to match. // Note: this matches *channels*, not channel *types*. (See ChannelTypeOf.) func ChannelOf(elementTypeMatcher *base.Matcher) *base.Matcher { match := func(actual interface{}) *base.Result { actualType := reflect.Typeof(actual) if channelType, ok := actualType.(*reflect.ChanType); ok { elementType := channelType.Elem() result := elementTypeMatcher.Match(elementType) return base.NewResultf(result.Matched(), "was channel with elements of type %v", elementType). WithCauses(result) } return base.NewResultf(false, "was of type %T, not a channel", actual) } return base.NewMatcherf(match, "ChannelOf(%v)", elementTypeMatcher) }
// Returns a new matcher that, on any input that is a pointer, extracts the // type of object that it thinks it's pointing to (the "pointee") and // matches it against the given matcher. // // If the given input is not an pointer, this fails to match. // Note: this matches *pointers*, not pointer *types*. (See PtrTypeTo.) func PtrTo(pointeeTypeMatcher *base.Matcher) *base.Matcher { match := func(actual interface{}) *base.Result { actualType := reflect.Typeof(actual) if ptrType, ok := actualType.(*reflect.PtrType); ok { elementType := ptrType.Elem() result := pointeeTypeMatcher.Match(elementType) return base.NewResultf( result.Matched(), "was PtrType to type %v", elementType). WithCauses(result) } return base.NewResultf(false, "was type %T, not a pointer", actual) } return base.NewMatcherf(match, "PtrTo(%v)", pointeeTypeMatcher) }
// Applies the given matcher to the result of writing the input object's // to a string by using fmt.Sprintf("%#v", object). func ToGoString(matcher *base.Matcher) *base.Matcher { match := func(actual interface{}) *base.Result { if gostringer, ok := actual.(fmt.GoStringer); ok { s := gostringer.GoString() result := matcher.Match(s) return base.NewResultf(result.Matched(), "GoString() returned %v", s). WithCauses(result) } s := fmt.Sprintf("%#v", actual) result := matcher.Match(s) return base.NewResultf(result.Matched(), "Not a fmt.GoStringer, but prints as %v", s). WithCauses(result) } return base.NewMatcherf(match, "ToGoString(%v)", matcher) }
// Creates a matcher that passes when neither this matcher nor the // other matcher pass. This operation is short-circuiting, so that // if the first matcher matches, the second is not attempted. // Note that this is logically equivalent to: // Both(Not(matcher1)).And(Not(matcher2)) // But may be more readable in practice. func (self *NeitherClause) Nor(matcher2 *base.Matcher) *base.Matcher { matcher1 := self.matcher match := func(actual interface{}) *base.Result { result1 := matcher1.Match(actual) if result1.Matched() { return base.NewResultf(false, "first part of 'Nor' matched [%v]", actual). WithCauses(result1) } result2 := matcher2.Match(actual) if result2.Matched() { return base.NewResultf(false, "second part of 'Nor' matched [%v]", actual). WithCauses(result2) } return base.NewResultf(true, "neither part of 'Nor' matched [%v]", actual). WithCauses(result1, result2) } return base.NewMatcherf(match, "neither [%v] nor [%v]", matcher1, matcher2) }
// Second part of a builder for a short-circuiting both/and matcher. func (self *BothClause) And(matcher2 *base.Matcher) *base.Matcher { matcher1 := self.matcher match := func(actual interface{}) *base.Result { result1 := matcher1.Match(actual) if !result1.Matched() { return base.NewResultf(false, "first part of 'Both/And' did not match [%v]", actual). WithCauses(result1) } result2 := matcher2.Match(actual) if !result2.Matched() { return base.NewResultf(false, "second part of 'Both/And' did not match [%v]", actual). WithCauses(result2) } return base.NewResultf(true, "both parts of 'Both/And' matched [%v]", actual). WithCauses(result1, result2) } return base.NewMatcherf(match, "both [%v] and [%v]", matcher1, matcher2) }
// Returns a matcher that matches on any array or slice input value // if the given matcher matches at least one element of that array // or slice. // // The returned matcher does not match any non-array-or-slice value. func AnyElem(matcher *base.Matcher) *base.Matcher { match := func(actual interface{}) *base.Result { v := reflect.NewValue(actual) if value, ok := v.(_ElemAndLen); ok { n := value.Len() for i := 0; i < n; i++ { elem := value.Elem(i).Interface() result := matcher.Match(elem) if result.Matched() { return base.NewResultf(true, "Matched element %v of %v: %v", i+1, n, elem). WithCauses(result) } } return base.NewResultf(false, "Matched none of the %v elements", n) } return matcher.Match(v) } return base.NewMatcherf(match, "AnyElement[%v]", matcher) }
// Constructs a short-circuiting if/then matcher: // matcher := If(AntecedentMatcher).Then(ConsequentMatcher) // such that the consequent is only tested when the antecedent // matches, and the resulting matcher only fails to match when the // consequent fails to match. Note that this is logically // equivalent to: // Either(Not(AntecedentMatcher)).Or(ConsequentMatcher) // But may be more readable in practice. func (self *IfClause) Then(consequent *base.Matcher) *base.Matcher { antecedent := self.antecedent match := func(actual interface{}) *base.Result { result1 := antecedent.Match(actual) if !result1.Matched() { return base.NewResultf(true, "'If/Then' matched because antecedent failed on [%v]", actual). WithCauses(result1) } result2 := consequent.Match(actual) if result2.Matched() { return base.NewResultf(true, "'If/Then' matched because consequent matched on [%v]", actual). WithCauses(result2) } return base.NewResultf(false, "'If/Then' failed on [%v]", actual). WithCauses(result1, result2) } return base.NewMatcherf(match, "if [%v] then [%v]", antecedent, consequent) }
// Returns a new matcher that, on any input that is a *reflect.MapType, // extracts the type of keys and element and matches them against two // given matchers. // // If the given input is not an *reflect.MapType, this fails to match. // Note: this matches map *types*, not maps. (See MapOf.) func MapTypeOf(keyTypeMatcher, elementTypeMatcher *base.Matcher) *base.Matcher { match := func(actual interface{}) *base.Result { if mapType, ok := actual.(*reflect.MapType); ok { keyType := mapType.Key() elementType := mapType.Elem() keyResult := keyTypeMatcher.Match(keyType) if !keyResult.Matched() { return base.NewResultf(false, "was MapType with keys of type %v", keyType). WithCauses(keyResult) } elementResult := elementTypeMatcher.Match(elementType) return base.NewResultf(elementResult.Matched(), "was MapType with keys/elements of type %v/%v", keyType, elementType). WithCauses(keyResult, elementResult) } return base.NewResultf(false, "was of type %T, not a MapType", actual) } return base.NewMatcherf(match, "MapTypeOf(%v, %v)", keyTypeMatcher, elementTypeMatcher) }
func checkMatcherIsNonMatchingOnNils(t *testing.T, matcher *base.Matcher) { checkResultIsNonMatching(t, matcher.Match(nil), "nil") checkResultIsNonMatching(t, matcher.Match(uninitialized._pointer), "uninitialized pointer") checkResultIsNonMatching(t, matcher.Match(uninitialized._func), "uninitialized func") checkResultIsNonMatching(t, matcher.Match(uninitialized._slice), "uninitialized slice") checkResultIsNonMatching(t, matcher.Match(uninitialized._chan), "uninitialized chan") checkResultIsNonMatching(t, matcher.Match(uninitialized._map), "uninitialized map") checkResultIsNonMatching(t, matcher.Match(uninitialized._interface), "uninitialized interface") }
func logSamples(t *testing.T, matcher *base.Matcher) { t.Logf("Sample results for: %v\n", matcher) t.Logf("\ton true: %v\n", matcher.Match(true)) t.Logf("\ton false: %v\n", matcher.Match(false)) t.Logf("\ton int: %v\n", matcher.Match(42)) t.Logf("\ton uint: %v\n", matcher.Match(uint(42))) t.Logf("\ton float: %v\n", matcher.Match(42.0)) t.Logf("\ton string: %v\n", matcher.Match("foobar")) t.Logf("\ton struct: %v\n", matcher.Match(struct{ Field int }{Field: 42})) t.Logf("\ton type: %v\n", matcher.Match(reflect.Typeof(uninitialized))) t.Logf("\ton channel: %v\n", matcher.Match(make(chan int, 1))) t.Logf("\ton function: %v\n", matcher.Match(func() int { return 1 })) t.Logf("\ton function: %v\n", matcher.Match(interface{}(nil))) t.Logf("\ton map: %v\n", matcher.Match(map[int]string{1: "one", 2: "two"})) t.Logf("\ton pointer: %v\n", matcher.Match(&struct{ Field int }{Field: 42})) t.Logf("\ton slice: %v\n", matcher.Match([]int{1})) t.Logf("\ton nil: %v\n", matcher.Match(nil)) t.Logf("\ton nil channel: %v\n", matcher.Match(uninitialized._chan)) t.Logf("\ton nil function: %v\n", matcher.Match(uninitialized._func)) t.Logf("\ton nil interface: %v\n", matcher.Match(uninitialized._interface)) t.Logf("\ton nil map: %v\n", matcher.Match(uninitialized._map)) t.Logf("\ton nil pointer: %v\n", matcher.Match(uninitialized._pointer)) t.Logf("\ton nil slice: %v\n", matcher.Match(uninitialized._slice)) }
func checkMatcherIsNonMatchingOnNils(t *testing.T, matcher *base.Matcher) { we := asserter.Using(t) we.CheckThat(matcher.Match(nil), DidNotMatch.Comment("nil")) we.CheckThat(matcher.Match(uninitialized._pointer), DidNotMatch.Comment("uninitialized pointer")) we.CheckThat(matcher.Match(uninitialized._func), DidNotMatch.Comment("uninitialized func")) we.CheckThat(matcher.Match(uninitialized._slice), DidNotMatch.Comment("uninitialized slice")) we.CheckThat(matcher.Match(uninitialized._chan), DidNotMatch.Comment("uninitialized chan")) we.CheckThat(matcher.Match(uninitialized._map), DidNotMatch.Comment("uninitialized map")) we.CheckThat(matcher.Match(uninitialized._interface), DidNotMatch.Comment("uninitialized interface")) }