error_test.go 6.6 KB

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