stack_test.go 4.7 KB

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