Skip to content
This repository was archived by the owner on Jun 27, 2023. It is now read-only.

Commit 0d51e72

Browse files
committed
Fix #71 Do signature change error msg
Update the error handling for Call.Do in the case where the argument passed to Call.Do does not match expectations. * panic if the argument is not a function * panic if the number of input arguments do not match those expected by Call * panic if the types of the input arguments do not match those expected by Call * panic if the number of return arguments do not match those expected by Call * panic if the types of return arguments do not match those expected by Call
1 parent 5c85495 commit 0d51e72

File tree

3 files changed

+247
-2
lines changed

3 files changed

+247
-2
lines changed

gomock/call.go

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,70 @@ func (c *Call) DoAndReturn(f interface{}) *Call {
130130
return c
131131
}
132132

133+
// validateInputAndOutputSig compares the argument and return signatures of the
134+
// function passed to Do against those expected by Call. It panics unless everything
135+
// matches.
136+
func validateInputAndOutputSig(doFunc, callFunc reflect.Type) {
137+
// check number of arguments and type of each argument
138+
if doFunc.NumIn() != callFunc.NumIn() {
139+
panic(
140+
fmt.Sprintf(
141+
"Do: expected function to have %d arguments not %d",
142+
callFunc.NumIn(), doFunc.NumIn()),
143+
)
144+
}
145+
146+
for i := 0; i < callFunc.NumIn(); i++ {
147+
if doFunc.In(i) != callFunc.In(i) {
148+
panic(
149+
fmt.Sprintf(
150+
"Do: expected function to have"+
151+
" arg of type %v at position %d",
152+
callFunc.In(i), i,
153+
),
154+
)
155+
}
156+
}
157+
158+
// check number of return vals and type of each val
159+
if doFunc.NumOut() != callFunc.NumOut() {
160+
panic(
161+
fmt.Sprintf(
162+
"Do: expected function to have %d return vals not %d",
163+
callFunc.NumOut(), doFunc.NumOut()),
164+
)
165+
}
166+
167+
for i := 0; i < callFunc.NumOut(); i++ {
168+
if doFunc.Out(i) != callFunc.Out(i) {
169+
panic(
170+
fmt.Sprintf(
171+
"Do: expected function to have"+
172+
" return val of type %v at position %d",
173+
callFunc.Out(i), i,
174+
),
175+
)
176+
}
177+
}
178+
}
179+
133180
// Do declares the action to run when the call is matched. The function's
134181
// return values are ignored to retain backward compatibility. To use the
135182
// return values call DoAndReturn.
136183
// It takes an interface{} argument to support n-arity functions.
137184
func (c *Call) Do(f interface{}) *Call {
138-
// TODO: Check arity and types here, rather than dying badly elsewhere.
139185
v := reflect.ValueOf(f)
140186

187+
switch v.Kind() {
188+
case reflect.Func:
189+
mt := c.methodType
190+
191+
ft := v.Type()
192+
validateInputAndOutputSig(ft, mt)
193+
default:
194+
panic("Do: argument must be a function")
195+
}
196+
141197
c.addAction(func(args []interface{}) []interface{} {
142198
vargs := make([]reflect.Value, len(args))
143199
ft := v.Type()

gomock/call_test.go

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package gomock
22

33
import (
4+
"reflect"
45
"testing"
56
)
67

@@ -49,3 +50,189 @@ func TestCall_After(t *testing.T) {
4950
}
5051
})
5152
}
53+
54+
func TestCall_Do(t *testing.T) {
55+
t.Run("Do function matches Call function", func(t *testing.T) {
56+
tr := &mockTestReporter{}
57+
58+
doFunc := func(x int) bool {
59+
if x < 20 {
60+
return false
61+
}
62+
63+
return true
64+
}
65+
66+
callFunc := func(x int) bool {
67+
return false
68+
}
69+
70+
c := &Call{
71+
t: tr,
72+
methodType: reflect.TypeOf(callFunc),
73+
}
74+
75+
c.Do(doFunc)
76+
77+
if len(c.actions) != 1 {
78+
t.Errorf("expected %d actions but got %d", 1, len(c.actions))
79+
}
80+
})
81+
82+
t.Run("argument to Do is not a function", func(t *testing.T) {
83+
tr := &mockTestReporter{}
84+
85+
callFunc := func(x int, y int) bool {
86+
return false
87+
}
88+
89+
c := &Call{
90+
t: tr,
91+
methodType: reflect.TypeOf(callFunc),
92+
}
93+
94+
defer func() {
95+
if r := recover(); r == nil {
96+
t.Error("expected Do to panic")
97+
}
98+
}()
99+
100+
c.Do("meow")
101+
102+
if len(c.actions) != 1 {
103+
t.Errorf("expected %d actions but got %d", 1, len(c.actions))
104+
}
105+
})
106+
107+
t.Run("number of args for Do func don't match Call func", func(t *testing.T) {
108+
tr := &mockTestReporter{}
109+
110+
doFunc := func(x int) bool {
111+
if x < 20 {
112+
return false
113+
}
114+
115+
return true
116+
}
117+
118+
callFunc := func(x int, y int) bool {
119+
return false
120+
}
121+
122+
c := &Call{
123+
t: tr,
124+
methodType: reflect.TypeOf(callFunc),
125+
}
126+
127+
defer func() {
128+
if r := recover(); r == nil {
129+
t.Error("expected Do to panic")
130+
}
131+
}()
132+
133+
c.Do(doFunc)
134+
135+
if len(c.actions) != 1 {
136+
t.Errorf("expected %d actions but got %d", 1, len(c.actions))
137+
}
138+
})
139+
140+
t.Run("arg types for Do func don't match Call func", func(t *testing.T) {
141+
tr := &mockTestReporter{}
142+
143+
doFunc := func(x int) bool {
144+
if x < 20 {
145+
return false
146+
}
147+
148+
return true
149+
}
150+
151+
callFunc := func(x string) bool {
152+
return false
153+
}
154+
155+
c := &Call{
156+
t: tr,
157+
methodType: reflect.TypeOf(callFunc),
158+
}
159+
160+
defer func() {
161+
if r := recover(); r == nil {
162+
t.Error("expected Do to panic")
163+
}
164+
}()
165+
166+
c.Do(doFunc)
167+
168+
if len(c.actions) != 1 {
169+
t.Errorf("expected %d actions but got %d", 1, len(c.actions))
170+
}
171+
})
172+
173+
t.Run("number of return vals for Do func don't match Call func", func(t *testing.T) {
174+
tr := &mockTestReporter{}
175+
176+
doFunc := func(x int) bool {
177+
if x < 20 {
178+
return false
179+
}
180+
181+
return true
182+
}
183+
184+
callFunc := func(x int) (bool, error) {
185+
return false, nil
186+
}
187+
188+
c := &Call{
189+
t: tr,
190+
methodType: reflect.TypeOf(callFunc),
191+
}
192+
193+
defer func() {
194+
if r := recover(); r == nil {
195+
t.Error("expected Do to panic")
196+
}
197+
}()
198+
199+
c.Do(doFunc)
200+
201+
if len(c.actions) != 1 {
202+
t.Errorf("expected %d actions but got %d", 1, len(c.actions))
203+
}
204+
})
205+
206+
t.Run("return types for Do func don't match Call func", func(t *testing.T) {
207+
tr := &mockTestReporter{}
208+
209+
doFunc := func(x int) bool {
210+
if x < 20 {
211+
return false
212+
}
213+
214+
return true
215+
}
216+
217+
callFunc := func(x int) error {
218+
return nil
219+
}
220+
221+
c := &Call{
222+
t: tr,
223+
methodType: reflect.TypeOf(callFunc),
224+
}
225+
226+
defer func() {
227+
if r := recover(); r == nil {
228+
t.Error("expected Do to panic")
229+
}
230+
}()
231+
232+
c.Do(doFunc)
233+
234+
if len(c.actions) != 1 {
235+
t.Errorf("expected %d actions but got %d", 1, len(c.actions))
236+
}
237+
})
238+
}

gomock/controller_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,9 +492,11 @@ func TestDo(t *testing.T) {
492492
doCalled := false
493493
var argument string
494494
ctrl.RecordCall(subject, "FooMethod", "argument").Do(
495-
func(arg string) {
495+
func(arg string) int {
496496
doCalled = true
497497
argument = arg
498+
499+
return 0
498500
})
499501
if doCalled {
500502
t.Error("Do() callback called too early.")

0 commit comments

Comments
 (0)