stack_test.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  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.init\n" +
  61. "\t.+/github.com/pkg/errors/stack_test.go",
  62. }, {
  63. Frame(0),
  64. "%s",
  65. "unknown",
  66. }, {
  67. Frame(0),
  68. "%+s",
  69. "unknown",
  70. }, {
  71. Frame(initpc),
  72. "%d",
  73. "9",
  74. }, {
  75. Frame(0),
  76. "%d",
  77. "0",
  78. }, {
  79. Frame(initpc),
  80. "%n",
  81. "init",
  82. }, {
  83. func() Frame {
  84. var x X
  85. return x.ptr()
  86. }(),
  87. "%n",
  88. `\(\*X\).ptr`,
  89. }, {
  90. func() Frame {
  91. var x X
  92. return x.val()
  93. }(),
  94. "%n",
  95. "X.val",
  96. }, {
  97. Frame(0),
  98. "%n",
  99. "",
  100. }, {
  101. Frame(initpc),
  102. "%v",
  103. "stack_test.go:9",
  104. }, {
  105. Frame(initpc),
  106. "%+v",
  107. "github.com/pkg/errors.init\n" +
  108. "\t.+/github.com/pkg/errors/stack_test.go:9",
  109. }, {
  110. Frame(0),
  111. "%v",
  112. "unknown:0",
  113. }}
  114. for _, tt := range tests {
  115. testFormatRegexp(t, tt.Frame, tt.format, tt.want)
  116. }
  117. }
  118. func TestFuncname(t *testing.T) {
  119. tests := []struct {
  120. name, want string
  121. }{
  122. {"", ""},
  123. {"runtime.main", "main"},
  124. {"github.com/pkg/errors.funcname", "funcname"},
  125. {"funcname", "funcname"},
  126. {"io.copyBuffer", "copyBuffer"},
  127. {"main.(*R).Write", "(*R).Write"},
  128. }
  129. for _, tt := range tests {
  130. got := funcname(tt.name)
  131. want := tt.want
  132. if got != want {
  133. t.Errorf("funcname(%q): want: %q, got %q", tt.name, want, got)
  134. }
  135. }
  136. }
  137. func TestTrimGOPATH(t *testing.T) {
  138. var tests = []struct {
  139. Frame
  140. want string
  141. }{{
  142. Frame(initpc),
  143. "github.com/pkg/errors/stack_test.go",
  144. }}
  145. for _, tt := range tests {
  146. pc := tt.Frame.pc()
  147. fn := runtime.FuncForPC(pc)
  148. file, _ := fn.FileLine(pc)
  149. got := trimGOPATH(fn.Name(), file)
  150. want := tt.want
  151. if want != got {
  152. t.Errorf("%v: want %q, got %q", tt.Frame, want, got)
  153. }
  154. }
  155. }
  156. func TestStackTrace(t *testing.T) {
  157. tests := []struct {
  158. err error
  159. want []string
  160. }{{
  161. New("ooh"), []string{
  162. "github.com/pkg/errors.TestStackTrace\n" +
  163. "\t.+/github.com/pkg/errors/stack_test.go:175",
  164. },
  165. }, {
  166. Wrap(New("ooh"), "ahh"), []string{
  167. "github.com/pkg/errors.TestStackTrace\n" +
  168. "\t.+/github.com/pkg/errors/stack_test.go:180", // this is the stack of Wrap, not New
  169. },
  170. }, {
  171. Cause(Wrap(New("ooh"), "ahh")), []string{
  172. "github.com/pkg/errors.TestStackTrace\n" +
  173. "\t.+/github.com/pkg/errors/stack_test.go:185", // this is the stack of New
  174. },
  175. }, {
  176. func() error { return New("ooh") }(), []string{
  177. `github.com/pkg/errors.(func·005|TestStackTrace.func1)` +
  178. "\n\t.+/github.com/pkg/errors/stack_test.go:190", // this is the stack of New
  179. "github.com/pkg/errors.TestStackTrace\n" +
  180. "\t.+/github.com/pkg/errors/stack_test.go:190", // this is the stack of New's caller
  181. },
  182. }, {
  183. Cause(func() error {
  184. return func() error {
  185. return Errorf("hello %s", fmt.Sprintf("world"))
  186. }()
  187. }()), []string{
  188. `github.com/pkg/errors.(func·006|TestStackTrace.func2.1)` +
  189. "\n\t.+/github.com/pkg/errors/stack_test.go:199", // this is the stack of Errorf
  190. `github.com/pkg/errors.(func·007|TestStackTrace.func2)` +
  191. "\n\t.+/github.com/pkg/errors/stack_test.go:200", // this is the stack of Errorf's caller
  192. "github.com/pkg/errors.TestStackTrace\n" +
  193. "\t.+/github.com/pkg/errors/stack_test.go:201", // this is the stack of Errorf's caller's caller
  194. },
  195. }}
  196. for _, tt := range tests {
  197. x, ok := tt.err.(interface {
  198. StackTrace() StackTrace
  199. })
  200. if !ok {
  201. t.Errorf("expected %#v to implement StackTrace() StackTrace", tt.err)
  202. continue
  203. }
  204. st := x.StackTrace()
  205. for j, want := range tt.want {
  206. testFormatRegexp(t, st[j], "%+v", want)
  207. }
  208. }
  209. }
  210. func stackTrace() StackTrace {
  211. const depth = 8
  212. var pcs [depth]uintptr
  213. n := runtime.Callers(1, pcs[:])
  214. var st stack = pcs[0:n]
  215. return st.StackTrace()
  216. }
  217. func TestStackTraceFormat(t *testing.T) {
  218. tests := []struct {
  219. StackTrace
  220. format string
  221. want string
  222. }{{
  223. nil,
  224. "%s",
  225. `\[\]`,
  226. }, {
  227. nil,
  228. "%v",
  229. `\[\]`,
  230. }, {
  231. nil,
  232. "%+v",
  233. "",
  234. }, {
  235. nil,
  236. "%#v",
  237. `\[\]errors.Frame\(nil\)`,
  238. }, {
  239. make(StackTrace, 0),
  240. "%s",
  241. `\[\]`,
  242. }, {
  243. make(StackTrace, 0),
  244. "%v",
  245. `\[\]`,
  246. }, {
  247. make(StackTrace, 0),
  248. "%+v",
  249. "",
  250. }, {
  251. make(StackTrace, 0),
  252. "%#v",
  253. `\[\]errors.Frame{}`,
  254. }, {
  255. stackTrace()[:2],
  256. "%s",
  257. `\[stack_test.go stack_test.go\]`,
  258. }, {
  259. stackTrace()[:2],
  260. "%v",
  261. `\[stack_test.go:228 stack_test.go:275\]`,
  262. }, {
  263. stackTrace()[:2],
  264. "%+v",
  265. "\n" +
  266. "github.com/pkg/errors.stackTrace\n" +
  267. "\t.+/github.com/pkg/errors/stack_test.go:228\n" +
  268. "github.com/pkg/errors.TestStackTraceFormat\n" +
  269. "\t.+/github.com/pkg/errors/stack_test.go:279",
  270. }, {
  271. stackTrace()[:2],
  272. "%#v",
  273. `\[\]errors.Frame{stack_test.go:228, stack_test.go:287}`,
  274. }}
  275. for _, tt := range tests {
  276. testFormatRegexp(t, tt.StackTrace, tt.format, tt.want)
  277. }
  278. }