stack_test.go 5.1 KB

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