Skip to content
This repository was archived by the owner on Dec 1, 2021. It is now read-only.

Commit 7896481

Browse files
authored
Extended stacktrace output (#49)
* Extended stacktrace output * Make format tests less sensitive to sourcecode prefix * use testFormatRegexp for all Sprintf tests * replace output test with sample output * work around the different fn.Name format in go 1.4 * fix windows tests when there is a drive letter in the source path
1 parent d4b5735 commit 7896481

File tree

5 files changed

+194
-66
lines changed

5 files changed

+194
-66
lines changed

errors.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ func (e _error) Format(s fmt.State, verb rune) {
8787
switch verb {
8888
case 'v':
8989
if s.Flag('+') {
90-
fmt.Fprintf(s, "%+v: ", e.Stacktrace()[0])
90+
io.WriteString(s, e.msg)
91+
fmt.Fprintf(s, "%+v", e.Stacktrace())
92+
return
9193
}
9294
fallthrough
9395
case 's':

example_test.go

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,22 @@ func ExampleNew_printf() {
1717
err := errors.New("whoops")
1818
fmt.Printf("%+v", err)
1919

20-
// Output: github.com/pkg/errors/example_test.go:17: whoops
20+
// Example output:
21+
// whoops
22+
// github.com/pkg/errors_test.ExampleNew_printf
23+
// /home/dfc/src/github.com/pkg/errors/example_test.go:17
24+
// testing.runExample
25+
// /home/dfc/go/src/testing/example.go:114
26+
// testing.RunExamples
27+
// /home/dfc/go/src/testing/example.go:38
28+
// testing.(*M).Run
29+
// /home/dfc/go/src/testing/testing.go:744
30+
// main.main
31+
// /github.com/pkg/errors/_test/_testmain.go:106
32+
// runtime.main
33+
// /home/dfc/go/src/runtime/proc.go:183
34+
// runtime.goexit
35+
// /home/dfc/go/src/runtime/asm_amd64.s:2059
2136
}
2237

2338
func ExampleWrap() {
@@ -44,14 +59,34 @@ func ExampleCause() {
4459
// error
4560
}
4661

47-
func ExampleCause_printf() {
62+
func ExampleWrap_extended() {
4863
err := fn()
4964
fmt.Printf("%+v\n", err)
5065

51-
// Output: github.com/pkg/errors/example_test.go:32: error
52-
// github.com/pkg/errors/example_test.go:33: inner
53-
// github.com/pkg/errors/example_test.go:34: middle
54-
// github.com/pkg/errors/example_test.go:35: outer
66+
// Example output:
67+
// error
68+
// github.com/pkg/errors_test.fn
69+
// /home/dfc/src/github.com/pkg/errors/example_test.go:47
70+
// github.com/pkg/errors_test.ExampleCause_printf
71+
// /home/dfc/src/github.com/pkg/errors/example_test.go:63
72+
// testing.runExample
73+
// /home/dfc/go/src/testing/example.go:114
74+
// testing.RunExamples
75+
// /home/dfc/go/src/testing/example.go:38
76+
// testing.(*M).Run
77+
// /home/dfc/go/src/testing/testing.go:744
78+
// main.main
79+
// /github.com/pkg/errors/_test/_testmain.go:104
80+
// runtime.main
81+
// /home/dfc/go/src/runtime/proc.go:183
82+
// runtime.goexit
83+
// /home/dfc/go/src/runtime/asm_amd64.s:2059
84+
// github.com/pkg/errors_test.fn
85+
// /home/dfc/src/github.com/pkg/errors/example_test.go:48: inner
86+
// github.com/pkg/errors_test.fn
87+
// /home/dfc/src/github.com/pkg/errors/example_test.go:49: middle
88+
// github.com/pkg/errors_test.fn
89+
// /home/dfc/src/github.com/pkg/errors/example_test.go:50: outer
5590
}
5691

5792
func ExampleWrapf() {
@@ -62,11 +97,26 @@ func ExampleWrapf() {
6297
// Output: oh noes #2: whoops
6398
}
6499

65-
func ExampleErrorf() {
100+
func ExampleErrorf_extended() {
66101
err := errors.Errorf("whoops: %s", "foo")
67102
fmt.Printf("%+v", err)
68103

69-
// Output: github.com/pkg/errors/example_test.go:66: whoops: foo
104+
// Example output:
105+
// whoops: foo
106+
// github.com/pkg/errors_test.ExampleErrorf
107+
// /home/dfc/src/github.com/pkg/errors/example_test.go:101
108+
// testing.runExample
109+
// /home/dfc/go/src/testing/example.go:114
110+
// testing.RunExamples
111+
// /home/dfc/go/src/testing/example.go:38
112+
// testing.(*M).Run
113+
// /home/dfc/go/src/testing/testing.go:744
114+
// main.main
115+
// /github.com/pkg/errors/_test/_testmain.go:102
116+
// runtime.main
117+
// /home/dfc/go/src/runtime/proc.go:183
118+
// runtime.goexit
119+
// /home/dfc/go/src/runtime/asm_amd64.s:2059
70120
}
71121

72122
func Example_stacktrace() {
@@ -82,5 +132,21 @@ func Example_stacktrace() {
82132
st := err.Stacktrace()
83133
fmt.Printf("%+v", st[0:2]) // top two frames
84134

85-
// Output: [github.com/pkg/errors/example_test.go:32 github.com/pkg/errors/example_test.go:77]
135+
// Example output:
136+
// github.com/pkg/errors_test.fn
137+
// /home/dfc/src/github.com/pkg/errors/example_test.go:47
138+
// github.com/pkg/errors_test.Example_stacktrace
139+
// /home/dfc/src/github.com/pkg/errors/example_test.go:127
140+
}
141+
142+
func ExampleCause_printf() {
143+
err := errors.Wrap(func() error {
144+
return func() error {
145+
return errors.Errorf("hello %s", fmt.Sprintf("world"))
146+
}()
147+
}(), "failed")
148+
149+
fmt.Printf("%v", err)
150+
151+
// Output: failed: hello world
86152
}

format_test.go

Lines changed: 70 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@ package errors
33
import (
44
"fmt"
55
"io"
6+
"regexp"
7+
"strings"
68
"testing"
79
)
810

9-
func TestFormat(t *testing.T) {
11+
func TestFormatNew(t *testing.T) {
1012
tests := []struct {
1113
error
1214
format string
1315
want string
1416
}{{
15-
1617
New("error"),
1718
"%s",
1819
"error",
@@ -23,8 +24,22 @@ func TestFormat(t *testing.T) {
2324
}, {
2425
New("error"),
2526
"%+v",
26-
"github.com/pkg/errors/format_test.go:24: error",
27-
}, {
27+
"error\n" +
28+
"github.com/pkg/errors.TestFormatNew\n" +
29+
"\t.+/github.com/pkg/errors/format_test.go:25",
30+
}}
31+
32+
for _, tt := range tests {
33+
testFormatRegexp(t, tt.error, tt.format, tt.want)
34+
}
35+
}
36+
37+
func TestFormatErrorf(t *testing.T) {
38+
tests := []struct {
39+
error
40+
format string
41+
want string
42+
}{{
2843
Errorf("%s", "error"),
2944
"%s",
3045
"error",
@@ -35,8 +50,22 @@ func TestFormat(t *testing.T) {
3550
}, {
3651
Errorf("%s", "error"),
3752
"%+v",
38-
"github.com/pkg/errors/format_test.go:36: error",
39-
}, {
53+
"error\n" +
54+
"github.com/pkg/errors.TestFormatErrorf\n" +
55+
"\t.+/github.com/pkg/errors/format_test.go:51",
56+
}}
57+
58+
for _, tt := range tests {
59+
testFormatRegexp(t, tt.error, tt.format, tt.want)
60+
}
61+
}
62+
63+
func TestFormatWrap(t *testing.T) {
64+
tests := []struct {
65+
error
66+
format string
67+
want string
68+
}{{
4069
Wrap(New("error"), "error2"),
4170
"%s",
4271
"error2: error",
@@ -47,13 +76,26 @@ func TestFormat(t *testing.T) {
4776
}, {
4877
Wrap(New("error"), "error2"),
4978
"%+v",
50-
"github.com/pkg/errors/format_test.go:48: error\n" +
51-
"github.com/pkg/errors/format_test.go:48: error2",
79+
"error\n" +
80+
"github.com/pkg/errors.TestFormatWrap\n" +
81+
"\t.+/github.com/pkg/errors/format_test.go:77",
5282
}, {
5383
Wrap(io.EOF, "error"),
5484
"%s",
5585
"error: EOF",
56-
}, {
86+
}}
87+
88+
for _, tt := range tests {
89+
testFormatRegexp(t, tt.error, tt.format, tt.want)
90+
}
91+
}
92+
93+
func TestFormatWrapf(t *testing.T) {
94+
tests := []struct {
95+
error
96+
format string
97+
want string
98+
}{{
5799
Wrapf(New("error"), "error%d", 2),
58100
"%s",
59101
"error2: error",
@@ -65,22 +107,35 @@ func TestFormat(t *testing.T) {
65107
Wrap(io.EOF, "error"),
66108
"%+v",
67109
"EOF\n" +
68-
"github.com/pkg/errors/format_test.go:65: error",
110+
"github.com/pkg/errors.TestFormatWrapf\n" +
111+
"\t.+/github.com/pkg/errors/format_test.go:107: error",
69112
}, {
70113
Wrapf(New("error"), "error%d", 2),
71114
"%v",
72115
"error2: error",
73116
}, {
74117
Wrapf(New("error"), "error%d", 2),
75118
"%+v",
76-
"github.com/pkg/errors/format_test.go:74: error\n" +
77-
"github.com/pkg/errors/format_test.go:74: error2",
119+
"error\n" +
120+
"github.com/pkg/errors.TestFormatWrapf\n" +
121+
"\t.+/github.com/pkg/errors/format_test.go:117",
78122
}}
79123

80124
for _, tt := range tests {
81-
got := fmt.Sprintf(tt.format, tt.error)
82-
if got != tt.want {
83-
t.Errorf("fmt.Sprintf(%q, err): got: %q, want: %q", tt.format, got, tt.want)
125+
testFormatRegexp(t, tt.error, tt.format, tt.want)
126+
}
127+
}
128+
129+
func testFormatRegexp(t *testing.T, arg interface{}, format, want string) {
130+
got := fmt.Sprintf(format, arg)
131+
lines := strings.SplitN(got, "\n", -1)
132+
for i, w := range strings.SplitN(want, "\n", -1) {
133+
match, err := regexp.MatchString(w, lines[i])
134+
if err != nil {
135+
t.Fatal(err)
136+
}
137+
if !match {
138+
t.Errorf("fmt.Sprintf(%q, err): got: %q, want: %q", format, got, want)
84139
}
85140
}
86141
}

stack.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func (f Frame) Format(s fmt.State, verb rune) {
5959
io.WriteString(s, "unknown")
6060
} else {
6161
file, _ := fn.FileLine(pc)
62-
io.WriteString(s, trimGOPATH(fn.Name(), file))
62+
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
6363
}
6464
default:
6565
io.WriteString(s, path.Base(f.file()))
@@ -84,7 +84,9 @@ func (st Stacktrace) Format(s fmt.State, verb rune) {
8484
case 'v':
8585
switch {
8686
case s.Flag('+'):
87-
fmt.Fprintf(s, "%+v", []Frame(st))
87+
for _, f := range st {
88+
fmt.Fprintf(s, "\n%+v", f)
89+
}
8890
case s.Flag('#'):
8991
fmt.Fprintf(s, "%#v", []Frame(st))
9092
default:

0 commit comments

Comments
 (0)