error_test.go 6.3 KB

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