parent
b89875607e
commit
18255f411e
@ -0,0 +1,35 @@ |
|||||||
|
package matchers |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"path" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
// Path is a pathname matcher.
|
||||||
|
//
|
||||||
|
// A value matches if it is the same as the matcher pattern or has the matcher
|
||||||
|
// pattern as a trailing component. For example,
|
||||||
|
// a pattern "abc/def" matches "abc/def" itself, "omg/abc/def",
|
||||||
|
// but not "abc/def/wtf", "abc/omg/def", or "xabc/def".
|
||||||
|
//
|
||||||
|
// Both the pattern and the value are sanitized using path.Clean() before use.
|
||||||
|
//
|
||||||
|
// The empty pattern "" matches only the empty value "".
|
||||||
|
type Path string |
||||||
|
|
||||||
|
// Matches returns whether x is the matching pathname itself or ends with the
|
||||||
|
// matching pathname, inside another directory.
|
||||||
|
func (p Path) Matches(x interface{}) bool { |
||||||
|
if s, ok := x.(string); ok { |
||||||
|
p1 := path.Clean(string(p)) |
||||||
|
s = path.Clean(s) |
||||||
|
return s == p1 || strings.HasSuffix(s, "/"+p1) |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
||||||
|
|
||||||
|
// String returns the string representation of this pathname matcher.
|
||||||
|
func (p Path) String() string { |
||||||
|
return fmt.Sprintf("<path suffix %#v>", path.Clean(string(p))) |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
package matchers |
||||||
|
|
||||||
|
import "testing" |
||||||
|
|
||||||
|
func TestPathMatcher_Matches(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
name string |
||||||
|
p Path |
||||||
|
x interface{} |
||||||
|
want bool |
||||||
|
}{ |
||||||
|
{"EmptyMatchesEmpty", "", "", true}, |
||||||
|
{"EmptyDoesNotMatchNonEmpty", "", "a", false}, |
||||||
|
{"EmptyDoesNotMatchNonEmptyEvenWithTrailingSlash", "", "a/", false}, |
||||||
|
{"NonEmptyDoesNotMatchEmpty", "a", "", false}, |
||||||
|
{"ExactIsOK", "abc/def", "abc/def", true}, |
||||||
|
{"SuffixIsOK", "abc/def", "omg/abc/def", true}, |
||||||
|
{"SubstringIsNotOK", "abc/def", "omg/abc/def/wtf", false}, |
||||||
|
{"PrefixIsNotOK", "abc/def", "abc/def/wtf", false}, |
||||||
|
{"InterveningElementIsNotOK", "abc/def", "abc/bbq/def", false}, |
||||||
|
{"GeneralNonMatch", "abc/def", "omg/wtf", false}, |
||||||
|
{"UncleanPattern", "abc//def/", "abc/def", true}, |
||||||
|
{"UncleanArg", "abc/def", "abc//def", true}, |
||||||
|
{"NonStringArg", "a", 'a', false}, |
||||||
|
} |
||||||
|
for _, tt := range tests { |
||||||
|
t.Run(tt.name, func(t *testing.T) { |
||||||
|
if got := tt.p.Matches(tt.x); got != tt.want { |
||||||
|
t.Errorf("Path.Matches() = %v, want %v", got, tt.want) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestPathMatcher_String(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
name string |
||||||
|
p Path |
||||||
|
want string |
||||||
|
}{ |
||||||
|
{"General", "abc/def", "<path suffix \"abc/def\">"}, |
||||||
|
{"Unclean", "abc//def/", "<path suffix \"abc/def\">"}, |
||||||
|
} |
||||||
|
for _, tt := range tests { |
||||||
|
t.Run(tt.name, func(t *testing.T) { |
||||||
|
if got := tt.p.String(); got != tt.want { |
||||||
|
t.Errorf("Path.String() = %v, want %v", got, tt.want) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,39 @@ |
|||||||
|
package matchers |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"reflect" |
||||||
|
) |
||||||
|
|
||||||
|
// Slice is a gomock matcher that matches elements of an array or
|
||||||
|
// slice against its own members at the corresponding positions.
|
||||||
|
// Each member item in a Slice may be a regular item or a gomock
|
||||||
|
// Matcher instance.
|
||||||
|
type Slice []interface{} |
||||||
|
|
||||||
|
// Matches returns whether x is a slice with matching elements.
|
||||||
|
func (sm Slice) Matches(x interface{}) bool { |
||||||
|
v := reflect.ValueOf(x) |
||||||
|
switch v.Kind() { |
||||||
|
case reflect.Slice, reflect.Array: // OK
|
||||||
|
default: |
||||||
|
return false |
||||||
|
} |
||||||
|
l := v.Len() |
||||||
|
if l != len(sm) { |
||||||
|
return false |
||||||
|
} |
||||||
|
for i, m := range sm { |
||||||
|
m1 := toMatcher(m) |
||||||
|
v1 := v.Index(i).Interface() |
||||||
|
if !m1.Matches(v1) { |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
// String returns the string representation of this slice matcher.
|
||||||
|
func (sm Slice) String() string { |
||||||
|
return fmt.Sprint([]interface{}(sm)) |
||||||
|
} |
@ -0,0 +1,114 @@ |
|||||||
|
package matchers |
||||||
|
|
||||||
|
import ( |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/golang/mock/gomock" |
||||||
|
) |
||||||
|
|
||||||
|
func TestSliceMatcher_Matches(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
name string |
||||||
|
sm Slice |
||||||
|
x interface{} |
||||||
|
want bool |
||||||
|
}{ |
||||||
|
{ |
||||||
|
"EmptyEqEmpty", |
||||||
|
Slice{}, |
||||||
|
[]interface{}{}, |
||||||
|
true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"EmptyNeNotEmpty", |
||||||
|
Slice{}, |
||||||
|
[]interface{}{1}, |
||||||
|
false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"NotEmptyNeEmpty", |
||||||
|
Slice{0}, |
||||||
|
[]interface{}{}, |
||||||
|
false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"CompareRawValuesUsingEqualityHappy", |
||||||
|
Slice{1, 2, 3}, |
||||||
|
[]interface{}{1, 2, 3}, |
||||||
|
true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"CompareRawValuesUsingEqualityUnhappy", |
||||||
|
Slice{1, 2, 3}, |
||||||
|
[]interface{}{1, 20, 3}, |
||||||
|
false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"CompareMatcherUsingItsMatchesHappy", |
||||||
|
Slice{gomock.Nil(), gomock.Eq(3)}, |
||||||
|
[]interface{}{nil, 3}, |
||||||
|
true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"CompareMatcherUsingItsMatchesUnhappy", |
||||||
|
Slice{gomock.Nil(), gomock.Eq(3)}, |
||||||
|
[]interface{}{0, 3}, |
||||||
|
false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"NestedHappy", |
||||||
|
Slice{Slice{3}, 30}, |
||||||
|
[]interface{}{[]interface{}{3}, 30}, |
||||||
|
true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"NestedUnhappy", |
||||||
|
Slice{Slice{3}, 30}, |
||||||
|
[]interface{}{[]interface{}{300}, 30}, |
||||||
|
false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"MatchSliceOfMoreSpecificTypes", |
||||||
|
Slice{1, 2, 3}, |
||||||
|
[]int{1, 2, 3}, |
||||||
|
true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"AcceptArraysToo", |
||||||
|
Slice{1, 2, 3}, |
||||||
|
[...]int{1, 2, 3}, |
||||||
|
true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"RejectString", |
||||||
|
Slice{'a', 'b', 'c'}, |
||||||
|
"abc", |
||||||
|
false, |
||||||
|
}, |
||||||
|
} |
||||||
|
for _, tt := range tests { |
||||||
|
t.Run(tt.name, func(t *testing.T) { |
||||||
|
if got := tt.sm.Matches(tt.x); got != tt.want { |
||||||
|
t.Errorf("Slice.Matches() = %v, want %v", got, tt.want) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestSliceMatcher_String(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
name string |
||||||
|
sm Slice |
||||||
|
want string |
||||||
|
}{ |
||||||
|
{"int", []interface{}{3, 5, 7}, "[3 5 7]"}, |
||||||
|
{"string", []interface{}{"omg", "wtf", "bbq"}, "[omg wtf bbq]"}, |
||||||
|
} |
||||||
|
for _, tt := range tests { |
||||||
|
t.Run(tt.name, func(t *testing.T) { |
||||||
|
if got := tt.sm.String(); got != tt.want { |
||||||
|
t.Errorf("Slice.String() = %v, want %v", got, tt.want) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
package matchers |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"reflect" |
||||||
|
"sort" |
||||||
|
"strings" |
||||||
|
) |
||||||
|
|
||||||
|
// Struct is a struct member matcher.
|
||||||
|
type Struct map[string]interface{} |
||||||
|
|
||||||
|
// Matches returns whether all specified members match.
|
||||||
|
func (sm Struct) Matches(x interface{}) bool { |
||||||
|
v := reflect.ValueOf(x) |
||||||
|
if v.Kind() == reflect.Ptr { |
||||||
|
v = v.Elem() |
||||||
|
} |
||||||
|
if v.Kind() != reflect.Struct { |
||||||
|
return false |
||||||
|
} |
||||||
|
for n, m := range sm { |
||||||
|
m1 := toMatcher(m) |
||||||
|
f := v.FieldByName(n) |
||||||
|
if f == (reflect.Value{}) { |
||||||
|
return false |
||||||
|
} |
||||||
|
f1 := f.Interface() |
||||||
|
if !m1.Matches(f1) { |
||||||
|
return false |
||||||
|
} |
||||||
|
} |
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
func (sm Struct) String() string { |
||||||
|
var fields sort.StringSlice |
||||||
|
for name := range sm { |
||||||
|
fields = append(fields, name) |
||||||
|
} |
||||||
|
sort.Sort(fields) |
||||||
|
for i, name := range fields { |
||||||
|
value := sm[name] |
||||||
|
var vs string |
||||||
|
if _, ok := value.(fmt.Stringer); ok { |
||||||
|
vs = fmt.Sprintf("%s", value) |
||||||
|
} else { |
||||||
|
vs = fmt.Sprintf("%v", value) |
||||||
|
} |
||||||
|
fields[i] = fmt.Sprintf("%s=%s", name, vs) |
||||||
|
} |
||||||
|
return "<struct " + strings.Join(fields, " ") + ">" |
||||||
|
} |
@ -0,0 +1,113 @@ |
|||||||
|
package matchers |
||||||
|
|
||||||
|
import ( |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/golang/mock/gomock" |
||||||
|
) |
||||||
|
|
||||||
|
type stringable int |
||||||
|
|
||||||
|
func (s stringable) String() string { |
||||||
|
return "omg" |
||||||
|
} |
||||||
|
|
||||||
|
func TestStructMatcher_Matches(t *testing.T) { |
||||||
|
type value struct { |
||||||
|
A int |
||||||
|
B string |
||||||
|
} |
||||||
|
tests := []struct { |
||||||
|
name string |
||||||
|
sm Struct |
||||||
|
x interface{} |
||||||
|
want bool |
||||||
|
}{ |
||||||
|
{ |
||||||
|
"EmptyMatchesEmpty", |
||||||
|
Struct{}, |
||||||
|
value{}, |
||||||
|
true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"EmptyMatchesAny", |
||||||
|
Struct{}, |
||||||
|
value{A: 3, B: "omg"}, |
||||||
|
true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"EmptyStillDoesNotMatchNonStruct", |
||||||
|
Struct{}, |
||||||
|
0, |
||||||
|
false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"RegularFieldValuesUseEq1", |
||||||
|
Struct{"A": 3, "B": "omg"}, |
||||||
|
value{A: 3, B: "omg"}, |
||||||
|
true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"RegularFieldValuesUseEq2", |
||||||
|
Struct{"A": 3, "B": "omg"}, |
||||||
|
value{A: 4, B: "omg"}, |
||||||
|
false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"MatchersAreUsedVerbatim1", |
||||||
|
Struct{"A": gomock.Not(3), "B": gomock.Eq("omg")}, |
||||||
|
value{A: 4, B: "omg"}, |
||||||
|
true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"MatchersAreUsedVerbatim2", |
||||||
|
Struct{"A": gomock.Not(3), "B": gomock.Eq("omg")}, |
||||||
|
value{A: 3, B: "omg"}, |
||||||
|
false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"UnspecifiedFieldsAreIgnored", |
||||||
|
Struct{"A": 3}, |
||||||
|
value{A: 3, B: "omg"}, |
||||||
|
true, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"MissingFieldsReturnFailure", |
||||||
|
Struct{"NOTFOUND": 3}, |
||||||
|
value{A: 3}, |
||||||
|
false, |
||||||
|
}, |
||||||
|
{ |
||||||
|
"DerefsPointer", |
||||||
|
Struct{"A": 3}, |
||||||
|
&value{A: 3}, |
||||||
|
true, |
||||||
|
}, |
||||||
|
} |
||||||
|
for _, tt := range tests { |
||||||
|
t.Run(tt.name, func(t *testing.T) { |
||||||
|
if got := tt.sm.Matches(tt.x); got != tt.want { |
||||||
|
t.Errorf("Struct.Matches() = %v, want %v", got, tt.want) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestStructMatcher_String(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
name string |
||||||
|
sm Struct |
||||||
|
want string |
||||||
|
}{ |
||||||
|
{"UsesStringer", Struct{"A": stringable(0)}, "<struct A=omg>"}, |
||||||
|
{"ReprIfNotStringable", Struct{"A": nil}, "<struct A=<nil>>"}, |
||||||
|
{"SortsByKey", Struct{"B": 3, "A": 4}, "<struct A=4 B=3>"}, |
||||||
|
} |
||||||
|
for _, tt := range tests { |
||||||
|
t.Run(tt.name, func(t *testing.T) { |
||||||
|
if got := tt.sm.String(); got != tt.want { |
||||||
|
t.Errorf("Struct.String() = %v, want %v", got, tt.want) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
package matchers |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/golang/mock/gomock" |
||||||
|
) |
||||||
|
|
||||||
|
func toMatcher(v interface{}) gomock.Matcher { |
||||||
|
if m, ok := v.(gomock.Matcher); ok { |
||||||
|
return m |
||||||
|
} |
||||||
|
return gomock.Eq(v) |
||||||
|
} |
Loading…
Reference in new issue