stack_test.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. package errors
  2. import (
  3. "fmt"
  4. "runtime"
  5. "testing"
  6. )
  7. var initpc, _, _, _ = runtime.Caller(0)
  8. func TestFrameLine(t *testing.T) {
  9. var tests = []struct {
  10. Frame
  11. want int
  12. }{{
  13. Frame(initpc),
  14. 9,
  15. }, {
  16. func() Frame {
  17. var pc, _, _, _ = runtime.Caller(0)
  18. return Frame(pc)
  19. }(),
  20. 20,
  21. }, {
  22. func() Frame {
  23. var pc, _, _, _ = runtime.Caller(1)
  24. return Frame(pc)
  25. }(),
  26. 28,
  27. }, {
  28. Frame(0), // invalid PC
  29. 0,
  30. }}
  31. for _, tt := range tests {
  32. got := tt.Frame.line()
  33. want := tt.want
  34. if want != got {
  35. t.Errorf("Frame(%v): want: %v, got: %v", uintptr(tt.Frame), want, got)
  36. }
  37. }
  38. }
  39. type X struct{}
  40. func (x X) val() Frame {
  41. var pc, _, _, _ = runtime.Caller(0)
  42. return Frame(pc)
  43. }
  44. func (x *X) ptr() Frame {
  45. var pc, _, _, _ = runtime.Caller(0)
  46. return Frame(pc)
  47. }
  48. func TestFrameFormat(t *testing.T) {
  49. var tests = []struct {
  50. Frame
  51. format string
  52. want string
  53. }{{
  54. Frame(initpc),
  55. "%s",
  56. "stack_test.go",
  57. }, {
  58. Frame(initpc),
  59. "%+s",
  60. "github.com/pkg/errors/stack_test.go",
  61. }, {
  62. Frame(0),
  63. "%s",
  64. "unknown",
  65. }, {
  66. Frame(0),
  67. "%+s",
  68. "unknown",
  69. }, {
  70. Frame(initpc),
  71. "%d",
  72. "9",
  73. }, {
  74. Frame(0),
  75. "%d",
  76. "0",
  77. }, {
  78. Frame(initpc),
  79. "%n",
  80. "init",
  81. }, {
  82. func() Frame {
  83. var x X
  84. return x.ptr()
  85. }(),
  86. "%n",
  87. "(*X).ptr",
  88. }, {
  89. func() Frame {
  90. var x X
  91. return x.val()
  92. }(),
  93. "%n",
  94. "X.val",
  95. }, {
  96. Frame(0),
  97. "%n",
  98. "",
  99. }, {
  100. Frame(initpc),
  101. "%v",
  102. "stack_test.go:9",
  103. }, {
  104. Frame(initpc),
  105. "%+v",
  106. "github.com/pkg/errors/stack_test.go:9",
  107. }, {
  108. Frame(0),
  109. "%v",
  110. "unknown:0",
  111. }}
  112. for _, tt := range tests {
  113. got := fmt.Sprintf(tt.format, tt.Frame)
  114. want := tt.want
  115. if want != got {
  116. t.Errorf("%v %q: want: %q, got: %q", tt.Frame, tt.format, want, got)
  117. }
  118. }
  119. }
  120. func TestFuncname(t *testing.T) {
  121. tests := []struct {
  122. name, want string
  123. }{
  124. {"", ""},
  125. {"runtime.main", "main"},
  126. {"github.com/pkg/errors.funcname", "funcname"},
  127. {"funcname", "funcname"},
  128. {"io.copyBuffer", "copyBuffer"},
  129. {"main.(*R).Write", "(*R).Write"},
  130. }
  131. for _, tt := range tests {
  132. got := funcname(tt.name)
  133. want := tt.want
  134. if got != want {
  135. t.Errorf("funcname(%q): want: %q, got %q", tt.name, want, got)
  136. }
  137. }
  138. }
  139. func TestTrimGOPATH(t *testing.T) {
  140. var tests = []struct {
  141. Frame
  142. want string
  143. }{{
  144. Frame(initpc),
  145. "github.com/pkg/errors/stack_test.go",
  146. }}
  147. for _, tt := range tests {
  148. pc := tt.Frame.pc()
  149. fn := runtime.FuncForPC(pc)
  150. file, _ := fn.FileLine(pc)
  151. got := trimGOPATH(fn.Name(), file)
  152. want := tt.want
  153. if want != got {
  154. t.Errorf("%v: want %q, got %q", tt.Frame, want, got)
  155. }
  156. }
  157. }
  158. func TestStacktrace(t *testing.T) {
  159. tests := []struct {
  160. err error
  161. want []string
  162. }{{
  163. New("ooh"), []string{
  164. "github.com/pkg/errors/stack_test.go:177",
  165. },
  166. }, {
  167. Wrap(New("ooh"), "ahh"), []string{
  168. "github.com/pkg/errors/stack_test.go:181", // this is the stack of Wrap, not New
  169. },
  170. }, {
  171. Cause(Wrap(New("ooh"), "ahh")), []string{
  172. "github.com/pkg/errors/stack_test.go:185", // this is the stack of New
  173. },
  174. }, {
  175. func() error { return New("ooh") }(), []string{
  176. "github.com/pkg/errors/stack_test.go:189", // this is the stack of New
  177. "github.com/pkg/errors/stack_test.go:189", // this is the stack of New's caller
  178. },
  179. }, {
  180. Cause(func() error {
  181. return func() error {
  182. return Errorf("hello %s", fmt.Sprintf("world"))
  183. }()
  184. }()), []string{
  185. "github.com/pkg/errors/stack_test.go:196", // this is the stack of Errorf
  186. "github.com/pkg/errors/stack_test.go:197", // this is the stack of Errorf's caller
  187. "github.com/pkg/errors/stack_test.go:198", // this is the stack of Errorf's caller's caller
  188. },
  189. }}
  190. for i, tt := range tests {
  191. x, ok := tt.err.(interface {
  192. Stacktrace() Stacktrace
  193. })
  194. if !ok {
  195. t.Errorf("expected %#v to implement Stacktrace() Stacktrace", tt.err)
  196. continue
  197. }
  198. st := x.Stacktrace()
  199. for j, want := range tt.want {
  200. frame := st[j]
  201. got := fmt.Sprintf("%+v", frame)
  202. if got != want {
  203. t.Errorf("test %d: frame %d: got %q, want %q", i, j, got, want)
  204. }
  205. }
  206. }
  207. }
  208. func stacktrace() Stacktrace {
  209. const depth = 8
  210. var pcs [depth]uintptr
  211. n := runtime.Callers(1, pcs[:])
  212. var st stack = pcs[0:n]
  213. return st.Stacktrace()
  214. }
  215. func TestStacktraceFormat(t *testing.T) {
  216. tests := []struct {
  217. Stacktrace
  218. format string
  219. want string
  220. }{{
  221. nil,
  222. "%s",
  223. "[]",
  224. }, {
  225. nil,
  226. "%v",
  227. "[]",
  228. }, {
  229. nil,
  230. "%+v",
  231. "[]",
  232. }, {
  233. nil,
  234. "%#v",
  235. "[]errors.Frame(nil)",
  236. }, {
  237. make(Stacktrace, 0),
  238. "%s",
  239. "[]",
  240. }, {
  241. make(Stacktrace, 0),
  242. "%v",
  243. "[]",
  244. }, {
  245. make(Stacktrace, 0),
  246. "%+v",
  247. "[]",
  248. }, {
  249. make(Stacktrace, 0),
  250. "%#v",
  251. "[]errors.Frame{}",
  252. }, {
  253. stacktrace()[:2],
  254. "%s",
  255. "[stack_test.go stack_test.go]",
  256. }, {
  257. stacktrace()[:2],
  258. "%v",
  259. "[stack_test.go:226 stack_test.go:273]",
  260. }, {
  261. stacktrace()[:2],
  262. "%+v",
  263. "[github.com/pkg/errors/stack_test.go:226 github.com/pkg/errors/stack_test.go:277]",
  264. }, {
  265. stacktrace()[:2],
  266. "%#v",
  267. "[]errors.Frame{stack_test.go:226, stack_test.go:281}",
  268. }}
  269. for i, tt := range tests {
  270. got := fmt.Sprintf(tt.format, tt.Stacktrace)
  271. if got != tt.want {
  272. t.Errorf("test %d: got: %q, want: %q", i+1, got, tt.want)
  273. }
  274. }
  275. }