@@ -24,6 +24,8 @@ import (
2424 "github.com/stretchr/testify/assert/yaml"
2525)
2626
27+ const stackFrameBufferSize = 10
28+
2729//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl"
2830
2931// TestingT is an interface wrapper around *testing.T
@@ -212,57 +214,73 @@ the problem actually occurred in calling code.*/
212214func CallerInfo () []string {
213215
214216 var pc uintptr
215- var ok bool
216217 var file string
217218 var line int
218219 var name string
219220
220221 callers := []string {}
221- for i := 0 ; ; i ++ {
222- pc , file , line , ok = runtime .Caller (i )
223- if ! ok {
224- // The breaks below failed to terminate the loop, and we ran off the
225- // end of the call stack.
226- break
227- }
222+ pcs := make ([]uintptr , stackFrameBufferSize )
223+ offset := 1
228224
229- // This is a huge edge case, but it will panic if this is the case, see #180
230- if file == "<autogenerated>" {
231- break
232- }
225+ for {
226+ n := runtime .Callers (offset , pcs )
233227
234- f := runtime .FuncForPC (pc )
235- if f == nil {
236- break
237- }
238- name = f .Name ()
239-
240- // testing.tRunner is the standard library function that calls
241- // tests. Subtests are called directly by tRunner, without going through
242- // the Test/Benchmark/Example function that contains the t.Run calls, so
243- // with subtests we should break when we hit tRunner, without adding it
244- // to the list of callers.
245- if name == "testing.tRunner" {
228+ if n == 0 {
246229 break
247230 }
248231
249- parts := strings .Split (file , "/" )
250- if len (parts ) > 1 {
251- filename := parts [len (parts )- 1 ]
252- dir := parts [len (parts )- 2 ]
253- if (dir != "assert" && dir != "mock" && dir != "require" ) || filename == "mock_test.go" {
254- callers = append (callers , fmt .Sprintf ("%s:%d" , file , line ))
232+ frames := runtime .CallersFrames (pcs [:n ])
233+
234+ for {
235+ frame , more := frames .Next ()
236+ pc = frame .PC
237+ file = frame .File
238+ line = frame .Line
239+
240+ // This is a huge edge case, but it will panic if this is the case, see #180
241+ if file == "<autogenerated>" {
242+ break
255243 }
256- }
257244
258- // Drop the package
259- segments := strings .Split (name , "." )
260- name = segments [len (segments )- 1 ]
261- if isTest (name , "Test" ) ||
262- isTest (name , "Benchmark" ) ||
263- isTest (name , "Example" ) {
264- break
245+ f := runtime .FuncForPC (pc )
246+ if f == nil {
247+ break
248+ }
249+ name = f .Name ()
250+
251+ // testing.tRunner is the standard library function that calls
252+ // tests. Subtests are called directly by tRunner, without going through
253+ // the Test/Benchmark/Example function that contains the t.Run calls, so
254+ // with subtests we should break when we hit tRunner, without adding it
255+ // to the list of callers.
256+ if name == "testing.tRunner" {
257+ break
258+ }
259+
260+ parts := strings .Split (file , "/" )
261+ if len (parts ) > 1 {
262+ filename := parts [len (parts )- 1 ]
263+ dir := parts [len (parts )- 2 ]
264+ if (dir != "assert" && dir != "mock" && dir != "require" ) || filename == "mock_test.go" {
265+ callers = append (callers , fmt .Sprintf ("%s:%d" , file , line ))
266+ }
267+ }
268+
269+ // Drop the package
270+ segments := strings .Split (name , "." )
271+ name = segments [len (segments )- 1 ]
272+ if isTest (name , "Test" ) ||
273+ isTest (name , "Benchmark" ) ||
274+ isTest (name , "Example" ) {
275+ break
276+ }
277+ if ! more {
278+ break
279+ }
265280 }
281+ // We know we already have less than a buffer's worth of frames
282+ offset += stackFrameBufferSize
283+
266284 }
267285
268286 return callers
0 commit comments