commit
2679066de3
@ -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