stack_test.go 5.4 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.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 i, tt := range tests {
  115. testFormatRegexp(t, i, 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 i, 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. testFormatRegexp(t, i, got, "%s", tt.want)
  151. }
  152. }
  153. func TestStackTrace(t *testing.T) {
  154. tests := []struct {
  155. err error
  156. want []string
  157. }{{
  158. New("ooh"), []string{
  159. "github.com/pkg/errors.TestStackTrace\n" +
  160. "\t.+/github.com/pkg/errors/stack_test.go:172",
  161. },
  162. }, {
  163. Wrap(New("ooh"), "ahh"), []string{
  164. "github.com/pkg/errors.TestStackTrace\n" +
  165. "\t.+/github.com/pkg/errors/stack_test.go:177", // this is the stack of Wrap, not New
  166. },
  167. }, {
  168. Cause(Wrap(New("ooh"), "ahh")), []string{
  169. "github.com/pkg/errors.TestStackTrace\n" +
  170. "\t.+/github.com/pkg/errors/stack_test.go:182", // this is the stack of New
  171. },
  172. }, {
  173. func() error { return New("ooh") }(), []string{
  174. `github.com/pkg/errors.(func·009|TestStackTrace.func1)` +
  175. "\n\t.+/github.com/pkg/errors/stack_test.go:187", // this is the stack of New
  176. "github.com/pkg/errors.TestStackTrace\n" +
  177. "\t.+/github.com/pkg/errors/stack_test.go:187", // 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.(func·010|TestStackTrace.func2.1)` +
  186. "\n\t.+/github.com/pkg/errors/stack_test.go:196", // this is the stack of Errorf
  187. `github.com/pkg/errors.(func·011|TestStackTrace.func2)` +
  188. "\n\t.+/github.com/pkg/errors/stack_test.go:197", // this is the stack of Errorf's caller
  189. "github.com/pkg/errors.TestStackTrace\n" +
  190. "\t.+/github.com/pkg/errors/stack_test.go:198", // this is the stack of Errorf's caller's caller
  191. },
  192. }}
  193. for i, tt := range tests {
  194. x, ok := tt.err.(interface {
  195. StackTrace() StackTrace
  196. })
  197. if !ok {
  198. t.Errorf("expected %#v to implement StackTrace() StackTrace", tt.err)
  199. continue
  200. }
  201. st := x.StackTrace()
  202. for j, want := range tt.want {
  203. testFormatRegexp(t, i, st[j], "%+v", want)
  204. }
  205. }
  206. }
  207. func stackTrace() StackTrace {
  208. const depth = 8
  209. var pcs [depth]uintptr
  210. n := runtime.Callers(1, pcs[:])
  211. var st stack = pcs[0:n]
  212. return st.StackTrace()
  213. }
  214. func TestStackTraceFormat(t *testing.T) {
  215. tests := []struct {
  216. StackTrace
  217. format string
  218. want string
  219. }{{
  220. nil,
  221. "%s",
  222. `\[\]`,
  223. }, {
  224. nil,
  225. "%v",
  226. `\[\]`,
  227. }, {
  228. nil,
  229. "%+v",
  230. "",
  231. }, {
  232. nil,
  233. "%#v",
  234. `\[\]errors.Frame\(nil\)`,
  235. }, {
  236. make(StackTrace, 0),
  237. "%s",
  238. `\[\]`,
  239. }, {
  240. make(StackTrace, 0),
  241. "%v",
  242. `\[\]`,
  243. }, {
  244. make(StackTrace, 0),
  245. "%+v",
  246. "",
  247. }, {
  248. make(StackTrace, 0),
  249. "%#v",
  250. `\[\]errors.Frame{}`,
  251. }, {
  252. stackTrace()[:2],
  253. "%s",
  254. `\[stack_test.go stack_test.go\]`,
  255. }, {
  256. stackTrace()[:2],
  257. "%v",
  258. `\[stack_test.go:225 stack_test.go:272\]`,
  259. }, {
  260. stackTrace()[:2],
  261. "%+v",
  262. "\n" +
  263. "github.com/pkg/errors.stackTrace\n" +
  264. "\t.+/github.com/pkg/errors/stack_test.go:225\n" +
  265. "github.com/pkg/errors.TestStackTraceFormat\n" +
  266. "\t.+/github.com/pkg/errors/stack_test.go:276",
  267. }, {
  268. stackTrace()[:2],
  269. "%#v",
  270. `\[\]errors.Frame{stack_test.go:225, stack_test.go:284}`,
  271. }}
  272. for i, tt := range tests {
  273. testFormatRegexp(t, i, tt.StackTrace, tt.format, tt.want)
  274. }
  275. }