error_test.go 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. package errors
  2. import (
  3. "bytes"
  4. "fmt"
  5. "io"
  6. "reflect"
  7. "runtime"
  8. "strings"
  9. "testing"
  10. )
  11. func BenchmarkStackFormat(b *testing.B) {
  12. b.ReportAllocs()
  13. for i := 0; i < b.N; i++ {
  14. func() {
  15. defer func() {
  16. err := recover()
  17. if err != 'a' {
  18. b.Fatal(err)
  19. }
  20. e := Errorf("hi")
  21. _ = string(e.Stack())
  22. }()
  23. a()
  24. }()
  25. }
  26. }
  27. func TestStackFormat(t *testing.T) {
  28. defer func() {
  29. err := recover()
  30. if err != 'a' {
  31. t.Fatal(err)
  32. }
  33. e, expected := Errorf("hi"), callers()
  34. bs := [][]uintptr{e.stack, expected}
  35. if err := compareStacks(bs[0], bs[1]); err != nil {
  36. t.Errorf("Stack didn't match")
  37. t.Errorf(err.Error())
  38. }
  39. stack := string(e.Stack())
  40. if !strings.Contains(stack, "a: b(5)") {
  41. t.Errorf("Stack trace does not contain source line: 'a: b(5)'")
  42. t.Errorf(stack)
  43. }
  44. if !strings.Contains(stack, "error_test.go:") {
  45. t.Errorf("Stack trace does not contain file name: 'error_test.go:'")
  46. t.Errorf(stack)
  47. }
  48. }()
  49. a()
  50. }
  51. func TestSkipWorks(t *testing.T) {
  52. defer func() {
  53. err := recover()
  54. if err != 'a' {
  55. t.Fatal(err)
  56. }
  57. bs := [][]uintptr{Wrap("hi", 2).stack, callersSkip(2)}
  58. if err := compareStacks(bs[0], bs[1]); err != nil {
  59. t.Errorf("Stack didn't match")
  60. t.Errorf(err.Error())
  61. }
  62. }()
  63. a()
  64. }
  65. func TestNew(t *testing.T) {
  66. err := New("foo")
  67. if err.Error() != "foo" {
  68. t.Errorf("Wrong message")
  69. }
  70. err = New(fmt.Errorf("foo"))
  71. if err.Error() != "foo" {
  72. t.Errorf("Wrong message")
  73. }
  74. bs := [][]uintptr{New("foo").stack, callers()}
  75. if err := compareStacks(bs[0], bs[1]); err != nil {
  76. t.Errorf("Stack didn't match")
  77. t.Errorf(err.Error())
  78. }
  79. if err.ErrorStack() != err.TypeName()+" "+err.Error()+"\n"+string(err.Stack()) {
  80. t.Errorf("ErrorStack is in the wrong format")
  81. }
  82. }
  83. func TestIs(t *testing.T) {
  84. if Is(nil, io.EOF) {
  85. t.Errorf("nil is an error")
  86. }
  87. if !Is(io.EOF, io.EOF) {
  88. t.Errorf("io.EOF is not io.EOF")
  89. }
  90. if !Is(io.EOF, New(io.EOF)) {
  91. t.Errorf("io.EOF is not New(io.EOF)")
  92. }
  93. if !Is(New(io.EOF), New(io.EOF)) {
  94. t.Errorf("New(io.EOF) is not New(io.EOF)")
  95. }
  96. if Is(io.EOF, fmt.Errorf("io.EOF")) {
  97. t.Errorf("io.EOF is fmt.Errorf")
  98. }
  99. custErr := errorWithCustomIs{
  100. Key: "TestForFun",
  101. Err: io.EOF,
  102. }
  103. shouldMatch := errorWithCustomIs{
  104. Key: "TestForFun",
  105. }
  106. shouldNotMatch := errorWithCustomIs{Key: "notOk"}
  107. if !Is(custErr, shouldMatch) {
  108. t.Errorf("custErr is not a TestForFun customError")
  109. }
  110. if Is(custErr, shouldNotMatch) {
  111. t.Errorf("custErr is a notOk customError")
  112. }
  113. if !Is(custErr, New(shouldMatch)) {
  114. t.Errorf("custErr is not a New(TestForFun customError)")
  115. }
  116. if Is(custErr, New(shouldNotMatch)) {
  117. t.Errorf("custErr is a New(notOk customError)")
  118. }
  119. if !Is(New(custErr), shouldMatch) {
  120. t.Errorf("New(custErr) is not a TestForFun customError")
  121. }
  122. if Is(New(custErr), shouldNotMatch) {
  123. t.Errorf("New(custErr) is a notOk customError")
  124. }
  125. if !Is(New(custErr), New(shouldMatch)) {
  126. t.Errorf("New(custErr) is not a New(TestForFun customError)")
  127. }
  128. if Is(New(custErr), New(shouldNotMatch)) {
  129. t.Errorf("New(custErr) is a New(notOk customError)")
  130. }
  131. }
  132. func TestWrapError(t *testing.T) {
  133. e := func() error {
  134. return Wrap("hi", 1)
  135. }()
  136. if e.Error() != "hi" {
  137. t.Errorf("Constructor with a string failed")
  138. }
  139. if Wrap(fmt.Errorf("yo"), 0).Error() != "yo" {
  140. t.Errorf("Constructor with an error failed")
  141. }
  142. if Wrap(e, 0) != e {
  143. t.Errorf("Constructor with an Error failed")
  144. }
  145. if Wrap(nil, 0) != nil {
  146. t.Errorf("Constructor with nil failed")
  147. }
  148. }
  149. func TestWrapPrefixError(t *testing.T) {
  150. e := func() error {
  151. return WrapPrefix("hi", "prefix", 1)
  152. }()
  153. if e.Error() != "prefix: hi" {
  154. t.Errorf("Constructor with a string failed")
  155. }
  156. if WrapPrefix(fmt.Errorf("yo"), "prefix", 0).Error() != "prefix: yo" {
  157. t.Errorf("Constructor with an error failed")
  158. }
  159. prefixed := WrapPrefix(e, "prefix", 0)
  160. original := e.(*Error)
  161. if prefixed.Err != original.Err || !reflect.DeepEqual(prefixed.stack, original.stack) || !reflect.DeepEqual(prefixed.frames, original.frames) || prefixed.Error() != "prefix: prefix: hi" {
  162. t.Errorf("Constructor with an Error failed")
  163. }
  164. if original.Error() == prefixed.Error() {
  165. t.Errorf("WrapPrefix changed the original error")
  166. }
  167. if WrapPrefix(nil, "prefix", 0) != nil {
  168. t.Errorf("Constructor with nil failed")
  169. }
  170. if !strings.HasSuffix(original.StackFrames()[0].File, "error_test.go") || strings.HasSuffix(original.StackFrames()[1].File, "error_test.go") {
  171. t.Errorf("Skip failed")
  172. }
  173. }
  174. func ExampleErrorf(x int) (int, error) {
  175. if x%2 == 1 {
  176. return 0, Errorf("can only halve even numbers, got %d", x)
  177. }
  178. return x / 2, nil
  179. }
  180. func ExampleWrapError() (error, error) {
  181. // Wrap io.EOF with the current stack-trace and return it
  182. return nil, Wrap(io.EOF, 0)
  183. }
  184. func ExampleWrapError_skip() {
  185. defer func() {
  186. if err := recover(); err != nil {
  187. // skip 1 frame (the deferred function) and then return the wrapped err
  188. err = Wrap(err, 1)
  189. }
  190. }()
  191. }
  192. func ExampleIs(reader io.Reader, buff []byte) {
  193. _, err := reader.Read(buff)
  194. if Is(err, io.EOF) {
  195. return
  196. }
  197. }
  198. func ExampleNew(UnexpectedEOF error) error {
  199. // calling New attaches the current stacktrace to the existing UnexpectedEOF error
  200. return New(UnexpectedEOF)
  201. }
  202. func ExampleWrap() error {
  203. if err := recover(); err != nil {
  204. return Wrap(err, 1)
  205. }
  206. return a()
  207. }
  208. func ExampleError_Error(err error) {
  209. fmt.Println(err.Error())
  210. }
  211. func ExampleError_ErrorStack(err error) {
  212. fmt.Println(err.(*Error).ErrorStack())
  213. }
  214. func ExampleError_Stack(err *Error) {
  215. fmt.Println(err.Stack())
  216. }
  217. func ExampleError_TypeName(err *Error) {
  218. fmt.Println(err.TypeName(), err.Error())
  219. }
  220. func ExampleError_StackFrames(err *Error) {
  221. for _, frame := range err.StackFrames() {
  222. fmt.Println(frame.File, frame.LineNumber, frame.Package, frame.Name)
  223. }
  224. }
  225. func a() error {
  226. b(5)
  227. return nil
  228. }
  229. func b(i int) {
  230. c()
  231. }
  232. func c() {
  233. panic('a')
  234. }
  235. // compareStacks will compare a stack created using the errors package (actual)
  236. // with a reference stack created with the callers function (expected). The
  237. // first entry is not compared since the actual and expected stacks cannot
  238. // be created at the exact same program counter position so the first entry
  239. // will always differ somewhat. Returns nil if the stacks are equal enough and
  240. // an error containing a detailed error message otherwise.
  241. func compareStacks(actual, expected []uintptr) error {
  242. if len(actual) != len(expected) {
  243. return stackCompareError("Stacks does not have equal length", actual, expected)
  244. }
  245. for i, pc := range actual {
  246. if i != 0 && pc != expected[i] {
  247. return stackCompareError(fmt.Sprintf("Stacks does not match entry %d (and maybe others)", i), actual, expected)
  248. }
  249. }
  250. return nil
  251. }
  252. func stackCompareError(msg string, actual, expected []uintptr) error {
  253. return fmt.Errorf("%s\nActual stack trace:\n%s\nExpected stack trace:\n%s", msg, readableStackTrace(actual), readableStackTrace(expected))
  254. }
  255. func callers() []uintptr {
  256. return callersSkip(1)
  257. }
  258. func callersSkip(skip int) []uintptr {
  259. callers := make([]uintptr, MaxStackDepth)
  260. length := runtime.Callers(skip+2, callers[:])
  261. return callers[:length]
  262. }
  263. func readableStackTrace(callers []uintptr) string {
  264. var result bytes.Buffer
  265. frames := callersToFrames(callers)
  266. for _, frame := range frames {
  267. result.WriteString(fmt.Sprintf("%s:%d (%#x)\n\t%s\n", frame.File, frame.Line, frame.PC, frame.Function))
  268. }
  269. return result.String()
  270. }
  271. func callersToFrames(callers []uintptr) []runtime.Frame {
  272. frames := make([]runtime.Frame, 0, len(callers))
  273. framesPtr := runtime.CallersFrames(callers)
  274. for {
  275. frame, more := framesPtr.Next()
  276. frames = append(frames, frame)
  277. if !more {
  278. return frames
  279. }
  280. }
  281. }
  282. type errorWithCustomIs struct {
  283. Key string
  284. Err error
  285. }
  286. func (ewci errorWithCustomIs) Error() string {
  287. return "["+ewci.Key+"]: " + ewci.Err.Error()
  288. }
  289. func (ewci errorWithCustomIs) Is(target error) bool {
  290. matched, ok := target.(errorWithCustomIs)
  291. return ok && matched.Key == ewci.Key
  292. }