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

Show file and line number of call during errors #25

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 14 additions & 10 deletions gomock/call.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ type Call struct {
method string // the name of the method
args []Matcher // the args
rets []interface{} // the return values (if any)
origin string // file and line number of call setup

preReqs []*Call // prerequisite calls

Expand Down Expand Up @@ -78,8 +79,8 @@ func (c *Call) Do(f interface{}) *Call {
func (c *Call) Return(rets ...interface{}) *Call {
mt := c.methodType()
if len(rets) != mt.NumOut() {
c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d",
c.receiver, c.method, len(rets), mt.NumOut())
c.t.Fatalf("wrong number of arguments to Return for %T.%v: got %d, want %d [%s]",
c.receiver, c.method, len(rets), mt.NumOut(), c.origin)
}
for i, ret := range rets {
if got, want := reflect.TypeOf(ret), mt.Out(i); got == want {
Expand All @@ -90,8 +91,8 @@ func (c *Call) Return(rets ...interface{}) *Call {
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
// ok
default:
c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable",
i, c.receiver, c.method, want)
c.t.Fatalf("argument %d to Return for %T.%v is nil, but %v is not nillable [%s]",
i, c.receiver, c.method, want, c.origin)
}
} else if got.AssignableTo(want) {
// Assignable type relation. Make the assignment now so that the generated code
Expand All @@ -100,8 +101,8 @@ func (c *Call) Return(rets ...interface{}) *Call {
v.Set(reflect.ValueOf(ret))
rets[i] = v.Interface()
} else {
c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v",
i, c.receiver, c.method, got, want)
c.t.Fatalf("wrong type of argument %d to Return for %T.%v: %v is not assignable to %v [%s]",
i, c.receiver, c.method, got, want, c.origin)
}
}

Expand All @@ -124,7 +125,8 @@ func (c *Call) SetArg(n int, value interface{}) *Call {
// TODO: This will break on variadic methods.
// We will need to check those at invocation time.
if n < 0 || n >= mt.NumIn() {
c.t.Fatalf("SetArg(%d, ...) called for a method with %d args", n, mt.NumIn())
c.t.Fatalf("SetArg(%d, ...) called for a method with %d args [%s]",
n, mt.NumIn(), c.origin)
}
// Permit setting argument through an interface.
// In the interface case, we don't (nay, can't) check the type here.
Expand All @@ -133,12 +135,14 @@ func (c *Call) SetArg(n int, value interface{}) *Call {
case reflect.Ptr:
dt := at.Elem()
if vt := reflect.TypeOf(value); !vt.AssignableTo(dt) {
c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v", n, vt, dt)
c.t.Fatalf("SetArg(%d, ...) argument is a %v, not assignable to %v [%s]",
n, vt, dt, c.origin)
}
case reflect.Interface:
// nothing to do
default:
c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface type %v", n, at)
c.t.Fatalf("SetArg(%d, ...) referring to argument of non-pointer non-interface type %v [%s]",
n, at, c.origin)
}
c.setArgs[n] = reflect.ValueOf(value)
return c
Expand Down Expand Up @@ -184,7 +188,7 @@ func (c *Call) String() string {
args[i] = arg.String()
}
arguments := strings.Join(args, ", ")
return fmt.Sprintf("%T.%v(%s)", c.receiver, c.method, arguments)
return fmt.Sprintf("%T.%v(%s) [%s]", c.receiver, c.method, arguments, c.origin)
}

// Tests if the given call matches the expected call.
Expand Down
19 changes: 16 additions & 3 deletions gomock/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,11 @@
// - Handle different argument/return types (e.g. ..., chan, map, interface).
package gomock

import "sync"
import (
"fmt"
"runtime"
"sync"
)

// A TestReporter is something that can be used to report test failures.
// It is satisfied by the standard library's *testing.T.
Expand Down Expand Up @@ -98,7 +102,8 @@ func (ctrl *Controller) RecordCall(receiver interface{}, method string, args ...
ctrl.mu.Lock()
defer ctrl.mu.Unlock()

call := &Call{t: ctrl.t, receiver: receiver, method: method, args: margs, minCalls: 1, maxCalls: 1}
origin := callerInfo(2)
call := &Call{t: ctrl.t, receiver: receiver, method: method, args: margs, origin: origin, minCalls: 1, maxCalls: 1}

ctrl.expectedCalls.Add(call)
return call
Expand All @@ -110,7 +115,8 @@ func (ctrl *Controller) Call(receiver interface{}, method string, args ...interf

expected := ctrl.expectedCalls.FindMatch(receiver, method, args)
if expected == nil {
ctrl.t.Fatalf("no matching expected call: %T.%v(%v)", receiver, method, args)
origin := callerInfo(2)
ctrl.t.Fatalf("no matching expected call: %T.%v(%v) [%s]", receiver, method, args, origin)
}

// Two things happen here:
Expand Down Expand Up @@ -165,3 +171,10 @@ func (ctrl *Controller) Finish() {
ctrl.t.Fatalf("aborting test due to missing call(s)")
}
}

func callerInfo(skip int) string {
if _, file, line, ok := runtime.Caller(skip + 1); ok {
return fmt.Sprintf("%s:%d", file, line)
}
return "unknown file"
}