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

Commit fce40a6

Browse files
committed
Print stack traces in more places
1 parent 24a1f94 commit fce40a6

File tree

3 files changed

+71
-16
lines changed

3 files changed

+71
-16
lines changed

gomock/call.go

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,17 @@ func (c *Call) Do(f interface{}) *Call {
159159

160160
// Return declares the values to be returned by the mocked function call.
161161
func (c *Call) Return(rets ...interface{}) *Call {
162+
skipFrames := 1
163+
162164
if h, ok := c.t.(testHelper); ok {
163165
h.Helper()
164166
}
165167

166168
mt := c.methodType
167169
if len(rets) != mt.NumOut() {
168-
c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]",
169-
c.receiver, c.method, len(rets), mt.NumOut(), c.origin)
170+
stackTraceStr := "\n\n" + currentStackTrace(skipFrames)
171+
c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]%+v",
172+
c.receiver, c.method, len(rets), mt.NumOut(), c.origin, stackTraceStr)
170173
}
171174
for i, ret := range rets {
172175
if got, want := reflect.TypeOf(ret), mt.Out(i); got == want {
@@ -177,8 +180,9 @@ func (c *Call) Return(rets ...interface{}) *Call {
177180
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
178181
// ok
179182
default:
180-
c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]",
181-
i, c.receiver, c.method, want, c.origin)
183+
stackTraceStr := "\n\n" + currentStackTrace(skipFrames)
184+
c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]%+v",
185+
i, c.receiver, c.method, want, c.origin, stackTraceStr)
182186
}
183187
} else if got.AssignableTo(want) {
184188
// Assignable type relation. Make the assignment now so that the generated code
@@ -187,8 +191,10 @@ func (c *Call) Return(rets ...interface{}) *Call {
187191
v.Set(reflect.ValueOf(ret))
188192
rets[i] = v.Interface()
189193
} else {
190-
c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]",
191-
i, c.receiver, c.method, got, want, c.origin)
194+
skipFrames := 1
195+
stackTraceStr := "\n\n" + currentStackTrace(skipFrames)
196+
c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]%+v",
197+
i, c.receiver, c.method, got, want, c.origin, stackTraceStr)
192198
}
193199
}
194200

@@ -209,6 +215,8 @@ func (c *Call) Times(n int) *Call {
209215
// indirected through a pointer. Or, in the case of a slice, SetArg
210216
// will copy value's elements into the nth argument.
211217
func (c *Call) SetArg(n int, value interface{}) *Call {
218+
skipFrames := 1
219+
212220
if h, ok := c.t.(testHelper); ok {
213221
h.Helper()
214222
}
@@ -217,8 +225,9 @@ func (c *Call) SetArg(n int, value interface{}) *Call {
217225
// TODO: This will break on variadic methods.
218226
// We will need to check those at invocation time.
219227
if n < 0 || n >= mt.NumIn() {
220-
c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]",
221-
n, mt.NumIn(), c.origin)
228+
stackTraceStr := "\n\n" + currentStackTrace(skipFrames)
229+
c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]%+v",
230+
n, mt.NumIn(), c.origin, stackTraceStr)
222231
}
223232
// Permit setting argument through an interface.
224233
// In the interface case, we don't (nay, can't) check the type here.
@@ -227,16 +236,18 @@ func (c *Call) SetArg(n int, value interface{}) *Call {
227236
case reflect.Ptr:
228237
dt := at.Elem()
229238
if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) {
230-
c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]",
231-
n, vt, dt, c.origin)
239+
stackTraceStr := "\n\n" + currentStackTrace(skipFrames)
240+
c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]%+v",
241+
n, vt, dt, c.origin, stackTraceStr)
232242
}
233243
case reflect.Interface:
234244
// nothing to do
235245
case reflect.Slice:
236246
// nothing to do
237247
default:
238-
c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface non-slice type %v [%s]",
239-
n, at, c.origin)
248+
stackTraceStr := "\n\n" + currentStackTrace(skipFrames)
249+
c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface non-slice type %v [%s]%+v",
250+
n, at, c.origin, stackTraceStr)
240251
}
241252

242253
c.addAction(func(args []interface{}) []interface{} {

gomock/controller.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,9 @@ func (ctrl *Controller) Call(receiver interface{}, method string, args ...interf
147147

148148
expected, err := ctrl.expectedCalls.FindMatch(receiver, method, args)
149149
if err != nil {
150-
skipFrames := 2
151150
origin := callerInfo(skipFrames)
152-
stackTraceString := "\n\n" + stackTraceStringFromError(err, skipFrames)
153-
ctrl.t.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s%+v", receiver, method, args, origin, err, stackTraceString)
151+
stackTraceStr := "\n\n" + stackTraceStringFromError(err, skipFrames)
152+
ctrl.t.Fatalf("Unexpected call to %T.%v(%v) at %s because: %s%+v", receiver, method, args, origin, err, stackTraceStr)
154153
}
155154

156155
// Two things happen here:
@@ -187,7 +186,9 @@ func (ctrl *Controller) Finish() {
187186
defer ctrl.mu.Unlock()
188187

189188
if ctrl.finished {
190-
ctrl.t.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.")
189+
skipFrames := 1
190+
stackTraceStr := "\n\n" + currentStackTrace(skipFrames)
191+
ctrl.t.Fatalf("Controller.Finish was called more than once. It has to be called exactly once.%+v", stackTraceStr)
191192
}
192193
ctrl.finished = true
193194

gomock/stack.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package gomock
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/pkg/errors"
9+
)
10+
11+
const skipFrames = 2
12+
13+
type stackTracer interface {
14+
StackTrace() errors.StackTrace
15+
}
16+
17+
func stackTraceStringFromError(err error, skipFrames int) string {
18+
if err, ok := err.(stackTracer); ok {
19+
return stackTraceString(err.StackTrace(), skipFrames)
20+
}
21+
return ""
22+
}
23+
24+
func currentStackTrace(skipFrames int) string {
25+
err := errors.New("fake error just to get stack")
26+
if err, ok := err.(stackTracer); ok {
27+
return stackTraceString(err.StackTrace(), skipFrames)
28+
}
29+
return ""
30+
}
31+
32+
func stackTraceString(stackTrace errors.StackTrace, skipFrames int) string {
33+
buffer := bytes.NewBufferString("")
34+
for i := skipFrames + 1; i < len(stackTrace); i++ {
35+
frame := stackTrace[i]
36+
buffer.WriteString(fmt.Sprintf("%+v\n", frame))
37+
filename := fmt.Sprintf("%s", frame)
38+
if strings.Contains(filename, "_test.go") {
39+
break
40+
}
41+
}
42+
return buffer.String()
43+
}

0 commit comments

Comments
 (0)