Skip to content

Commit

Permalink
Add Slice, Rest, and RestType
Browse files Browse the repository at this point in the history
Slice matches against slices with specific elements. Rest indicates
there may be one or more optional elements following the required ones.
  • Loading branch information
willfaught committed May 18, 2016
1 parent 45e824e commit 871ea65
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 0 deletions.
79 changes: 79 additions & 0 deletions mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,85 @@ func AnyIf(f func(interface{}) bool) AnyIfType {
return AnyIfType(f)
}

// RestType indicates there may optionally be one or more remaining elements.
type RestType string

// Rest indicates there may optionally be one or more remaining elements.
//
// Example:
// mock.When("MyMethod", mock.Slice(123, mock.Rest)).Return(0)
const Rest RestType = "mock.rest"

func match(actual, expected interface{}) bool {
switch expected := expected.(type) {
case AnyType:
return true

case AnyIfType:
return expected(actual)

case AnythingOfType:
return reflect.TypeOf(actual).String() == string(expected)

default:
if expected == nil {
if actual == nil {
return true
} else {
var v = reflect.ValueOf(actual)

return v.CanInterface() && v.IsNil()
}
} else {
if reflect.DeepEqual(actual, expected) {
return true
} else {
return reflect.ValueOf(actual) == reflect.ValueOf(expected)
}
}
}

return false
}

// Slice is a helper to define AnyIfType arguments for slices and their elements.
//
// Example:
// mock.When("MyMethod", mock.Slice(123, mock.Rest)).Return(0)
func Slice(elements ...interface{}) AnyIfType {
return AnyIf(func(argument interface{}) bool {
var v = reflect.ValueOf(argument)

if v.Kind() != reflect.Slice {
return false
}

var el, al = len(elements), v.Len()

if el == 0 {
return al == 0
}

if elements[el-1] == Rest {
el--

if al < el {
return false
}
} else if al != el {
return false
}

for i := 0; i < el; i++ {
if !match(v.Index(i).Interface(), elements[i]) {
return false
}
}

return true
})
}

// Verify verifies the restrictions set in the stubbing.
func (m *Mock) Verify() (bool, error) {
for i, f := range m.Functions {
Expand Down
95 changes: 95 additions & 0 deletions mock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -786,3 +786,98 @@ func TestVerifyMocks(t *testing.T) {
t.Errorf("Expected verification error %s, found %s", bad1Error, err)
}
}

func TestSlice(t *testing.T) {
var m = &MockedStruct{}

t.Log("Match")

for _, test := range []struct {
a, e []interface{}
}{
{nil, nil},
{nil, []interface{}{}},
{nil, []interface{}{Rest}},
{[]interface{}{}, nil},
{[]interface{}{}, []interface{}{}},
{[]interface{}{}, []interface{}{Rest}},

{[]interface{}{1}, []interface{}{1}},
{[]interface{}{1}, []interface{}{Rest}},
{[]interface{}{1}, []interface{}{1, Rest}},

{[]interface{}{1, 2}, []interface{}{1, 2}},
{[]interface{}{1, 2}, []interface{}{Rest}},
{[]interface{}{1, 2}, []interface{}{1, Rest}},
{[]interface{}{1, 2}, []interface{}{1, 2, Rest}},

{[]interface{}{1, 2, 3}, []interface{}{1, 2, 3}},
{[]interface{}{1, 2, 3}, []interface{}{Rest}},
{[]interface{}{1, 2, 3}, []interface{}{1, Rest}},
{[]interface{}{1, 2, 3}, []interface{}{1, 2, Rest}},
{[]interface{}{1, 2, 3}, []interface{}{1, 2, 3, Rest}},
} {
t.Log("Test:", test)

m.Reset()
m.When("FuncVariadic", 1, Slice(test.e...))
m.FuncVariadic(1, test.a...)
}

t.Log("No match")

var try = func(f func()) (panicked bool) {
defer func() {
if v := recover(); v != nil {
panicked = true
}

return
}()

f()

return false
}

for _, test := range []struct {
a, e []interface{}
}{
{nil, []interface{}{1}},
{nil, []interface{}{1, Rest}},
{nil, []interface{}{1, 2}},
{nil, []interface{}{1, 2, Rest}},
{nil, []interface{}{1, 2, 3}},
{nil, []interface{}{1, 2, 3, Rest}},

{[]interface{}{}, []interface{}{1}},
{[]interface{}{}, []interface{}{1, Rest}},
{[]interface{}{}, []interface{}{1, 2}},
{[]interface{}{}, []interface{}{1, 2, Rest}},
{[]interface{}{}, []interface{}{1, 2, 3}},
{[]interface{}{}, []interface{}{1, 2, 3, Rest}},

{[]interface{}{1}, []interface{}{1, 2}},
{[]interface{}{1}, []interface{}{1, 2, Rest}},
{[]interface{}{1}, []interface{}{1, 2, 3}},
{[]interface{}{1}, []interface{}{1, 2, 3, Rest}},

{[]interface{}{1, 2}, []interface{}{1, 2, 3}},
{[]interface{}{1, 2}, []interface{}{1, 2, 3, Rest}},

{[]interface{}{1, 2}, []interface{}{2, 1}},
{[]interface{}{1, 2}, []interface{}{2, 1, Rest}},

{[]interface{}{1, 2, 3}, []interface{}{3, 2, 1}},
{[]interface{}{1, 2, 3}, []interface{}{3, 2, 1, Rest}},
} {
t.Log("Test:", test)

m.Reset()
m.When("FuncVariadic", 1, Slice(test.e...))

if !try(func() { m.FuncVariadic(1, test.a...) }) {
t.Errorf("Actual no panic, expected panic")
}
}
}

0 comments on commit 871ea65

Please sign in to comment.