format_test.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. package errors
  2. import (
  3. "errors"
  4. "fmt"
  5. "io"
  6. "regexp"
  7. "strings"
  8. "testing"
  9. )
  10. func TestFormatNew(t *testing.T) {
  11. tests := []struct {
  12. error
  13. format string
  14. want string
  15. }{{
  16. New("error"),
  17. "%s",
  18. "error",
  19. }, {
  20. New("error"),
  21. "%v",
  22. "error",
  23. }, {
  24. New("error"),
  25. "%+v",
  26. "error\n" +
  27. "github.com/pkg/errors.TestFormatNew\n" +
  28. "\t.+/github.com/pkg/errors/format_test.go:25",
  29. }, {
  30. New("error"),
  31. "%q",
  32. `"error"`,
  33. "\t.+/github.com/pkg/errors/format_test.go:26",
  34. }}
  35. for i, tt := range tests {
  36. testFormatRegexp(t, i, tt.error, tt.format, tt.want)
  37. }
  38. }
  39. func TestFormatErrorf(t *testing.T) {
  40. tests := []struct {
  41. error
  42. format string
  43. want string
  44. }{{
  45. Errorf("%s", "error"),
  46. "%s",
  47. "error",
  48. }, {
  49. Errorf("%s", "error"),
  50. "%v",
  51. "error",
  52. }, {
  53. Errorf("%s", "error"),
  54. "%+v",
  55. "error\n" +
  56. "github.com/pkg/errors.TestFormatErrorf\n" +
  57. "\t.+/github.com/pkg/errors/format_test.go:55",
  58. }}
  59. for i, tt := range tests {
  60. testFormatRegexp(t, i, tt.error, tt.format, tt.want)
  61. }
  62. }
  63. func TestFormatWrap(t *testing.T) {
  64. tests := []struct {
  65. error
  66. format string
  67. want string
  68. }{{
  69. Wrap(New("error"), "error2"),
  70. "%s",
  71. "error2: error",
  72. }, {
  73. Wrap(New("error"), "error2"),
  74. "%v",
  75. "error2: error",
  76. }, {
  77. Wrap(New("error"), "error2"),
  78. "%+v",
  79. "error\n" +
  80. "github.com/pkg/errors.TestFormatWrap\n" +
  81. "\t.+/github.com/pkg/errors/format_test.go:81",
  82. }, {
  83. Wrap(io.EOF, "error"),
  84. "%s",
  85. "error: EOF",
  86. }, {
  87. Wrap(io.EOF, "error"),
  88. "%v",
  89. "error: EOF",
  90. }, {
  91. Wrap(io.EOF, "error"),
  92. "%+v",
  93. "EOF\n" +
  94. "error\n" +
  95. "github.com/pkg/errors.TestFormatWrap\n" +
  96. "\t.+/github.com/pkg/errors/format_test.go:95",
  97. }, {
  98. Wrap(Wrap(io.EOF, "error1"), "error2"),
  99. "%+v",
  100. "EOF\n" +
  101. "error1\n" +
  102. "github.com/pkg/errors.TestFormatWrap\n" +
  103. "\t.+/github.com/pkg/errors/format_test.go:102\n",
  104. }, {
  105. Wrap(New("error with space"), "context"),
  106. "%q",
  107. `"context: error with space"`,
  108. }}
  109. for i, tt := range tests {
  110. testFormatRegexp(t, i, tt.error, tt.format, tt.want)
  111. }
  112. }
  113. func TestFormatWrapf(t *testing.T) {
  114. tests := []struct {
  115. error
  116. format string
  117. want string
  118. }{{
  119. Wrapf(io.EOF, "error%d", 2),
  120. "%s",
  121. "error2: EOF",
  122. }, {
  123. Wrapf(io.EOF, "error%d", 2),
  124. "%v",
  125. "error2: EOF",
  126. }, {
  127. Wrapf(io.EOF, "error%d", 2),
  128. "%+v",
  129. "EOF\n" +
  130. "error2\n" +
  131. "github.com/pkg/errors.TestFormatWrapf\n" +
  132. "\t.+/github.com/pkg/errors/format_test.go:133",
  133. }, {
  134. Wrapf(New("error"), "error%d", 2),
  135. "%s",
  136. "error2: error",
  137. }, {
  138. Wrapf(New("error"), "error%d", 2),
  139. "%v",
  140. "error2: error",
  141. }, {
  142. Wrapf(New("error"), "error%d", 2),
  143. "%+v",
  144. "error\n" +
  145. "github.com/pkg/errors.TestFormatWrapf\n" +
  146. "\t.+/github.com/pkg/errors/format_test.go:148",
  147. }}
  148. for i, tt := range tests {
  149. testFormatRegexp(t, i, tt.error, tt.format, tt.want)
  150. }
  151. }
  152. func TestFormatWithStack(t *testing.T) {
  153. tests := []struct {
  154. error
  155. format string
  156. want []string
  157. }{{
  158. WithStack(io.EOF),
  159. "%s",
  160. []string{"EOF"},
  161. }, {
  162. WithStack(io.EOF),
  163. "%v",
  164. []string{"EOF"},
  165. }, {
  166. WithStack(io.EOF),
  167. "%+v",
  168. []string{"EOF",
  169. "github.com/pkg/errors.TestFormatWithStack\n" +
  170. "\t.+/github.com/pkg/errors/format_test.go:171"},
  171. }, {
  172. WithStack(New("error")),
  173. "%s",
  174. []string{"error"},
  175. }, {
  176. WithStack(New("error")),
  177. "%v",
  178. []string{"error"},
  179. }, {
  180. WithStack(New("error")),
  181. "%+v",
  182. []string{"error",
  183. "github.com/pkg/errors.TestFormatWithStack\n" +
  184. "\t.+/github.com/pkg/errors/format_test.go:185",
  185. "github.com/pkg/errors.TestFormatWithStack\n" +
  186. "\t.+/github.com/pkg/errors/format_test.go:185"},
  187. }, {
  188. WithStack(WithStack(io.EOF)),
  189. "%+v",
  190. []string{"EOF",
  191. "github.com/pkg/errors.TestFormatWithStack\n" +
  192. "\t.+/github.com/pkg/errors/format_test.go:193",
  193. "github.com/pkg/errors.TestFormatWithStack\n" +
  194. "\t.+/github.com/pkg/errors/format_test.go:193"},
  195. }, {
  196. WithStack(WithStack(Wrapf(io.EOF, "message"))),
  197. "%+v",
  198. []string{"EOF",
  199. "message",
  200. "github.com/pkg/errors.TestFormatWithStack\n" +
  201. "\t.+/github.com/pkg/errors/format_test.go:201",
  202. "github.com/pkg/errors.TestFormatWithStack\n" +
  203. "\t.+/github.com/pkg/errors/format_test.go:201",
  204. "github.com/pkg/errors.TestFormatWithStack\n" +
  205. "\t.+/github.com/pkg/errors/format_test.go:201"},
  206. }, {
  207. WithStack(Errorf("error%d", 1)),
  208. "%+v",
  209. []string{"error1",
  210. "github.com/pkg/errors.TestFormatWithStack\n" +
  211. "\t.+/github.com/pkg/errors/format_test.go:212",
  212. "github.com/pkg/errors.TestFormatWithStack\n" +
  213. "\t.+/github.com/pkg/errors/format_test.go:212"},
  214. }}
  215. for i, tt := range tests {
  216. testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want)
  217. }
  218. }
  219. func TestFormatWithMessage(t *testing.T) {
  220. tests := []struct {
  221. error
  222. format string
  223. want []string
  224. }{{
  225. WithMessage(New("error"), "error2"),
  226. "%s",
  227. []string{"error2: error"},
  228. }, {
  229. WithMessage(New("error"), "error2"),
  230. "%v",
  231. []string{"error2: error"},
  232. }, {
  233. WithMessage(New("error"), "error2"),
  234. "%+v",
  235. []string{
  236. "error",
  237. "github.com/pkg/errors.TestFormatWithMessage\n" +
  238. "\t.+/github.com/pkg/errors/format_test.go:240",
  239. "error2"},
  240. }, {
  241. WithMessage(io.EOF, "addition1"),
  242. "%s",
  243. []string{"addition1: EOF"},
  244. }, {
  245. WithMessage(io.EOF, "addition1"),
  246. "%v",
  247. []string{"addition1: EOF"},
  248. }, {
  249. WithMessage(io.EOF, "addition1"),
  250. "%+v",
  251. []string{"EOF", "addition1"},
  252. }, {
  253. WithMessage(WithMessage(io.EOF, "addition1"), "addition2"),
  254. "%v",
  255. []string{"addition2: addition1: EOF"},
  256. }, {
  257. WithMessage(WithMessage(io.EOF, "addition1"), "addition2"),
  258. "%+v",
  259. []string{"EOF", "addition1", "addition2"},
  260. }, {
  261. Wrap(WithMessage(io.EOF, "error1"), "error2"),
  262. "%+v",
  263. []string{"EOF", "error1", "error2",
  264. "github.com/pkg/errors.TestFormatWithMessage\n" +
  265. "\t.+/github.com/pkg/errors/format_test.go:268"},
  266. }, {
  267. WithMessage(Errorf("error%d", 1), "error2"),
  268. "%+v",
  269. []string{"error1",
  270. "github.com/pkg/errors.TestFormatWithMessage\n" +
  271. "\t.+/github.com/pkg/errors/format_test.go:274",
  272. "error2"},
  273. }, {
  274. WithMessage(WithStack(io.EOF), "error"),
  275. "%+v",
  276. []string{
  277. "EOF",
  278. "github.com/pkg/errors.TestFormatWithMessage\n" +
  279. "\t.+/github.com/pkg/errors/format_test.go:281",
  280. "error"},
  281. }, {
  282. WithMessage(Wrap(WithStack(io.EOF), "inside-error"), "outside-error"),
  283. "%+v",
  284. []string{
  285. "EOF",
  286. "github.com/pkg/errors.TestFormatWithMessage\n" +
  287. "\t.+/github.com/pkg/errors/format_test.go:289",
  288. "inside-error",
  289. "github.com/pkg/errors.TestFormatWithMessage\n" +
  290. "\t.+/github.com/pkg/errors/format_test.go:289",
  291. "outside-error"},
  292. }}
  293. for i, tt := range tests {
  294. testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want)
  295. }
  296. }
  297. func testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) {
  298. got := fmt.Sprintf(format, arg)
  299. gotLines := strings.SplitN(got, "\n", -1)
  300. wantLines := strings.SplitN(want, "\n", -1)
  301. if len(wantLines) > len(gotLines) {
  302. t.Errorf("test %d: wantLines(%d) > gotLines(%d):\n got: %q\nwant: %q", n+1, len(wantLines), len(gotLines), got, want)
  303. return
  304. }
  305. for i, w := range wantLines {
  306. match, err := regexp.MatchString(w, gotLines[i])
  307. if err != nil {
  308. t.Fatal(err)
  309. }
  310. if !match {
  311. t.Errorf("test %d: line %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, got, want)
  312. }
  313. }
  314. }
  315. var stackLineR = regexp.MustCompile(`\.`)
  316. // parseBlocks parses input into a slice, where:
  317. // - incase entry contains a newline, its a stacktrace
  318. // - incase entry contains no newline, its a solo line.
  319. //
  320. // Example use:
  321. //
  322. // for _, e := range blocks {
  323. // if strings.ContainsAny(e, "\n") {
  324. // // Match as stack
  325. // } else {
  326. // // Match as line
  327. // }
  328. // }
  329. func parseBlocks(input string) ([]string, error) {
  330. var blocks []string
  331. stack := ""
  332. wasStack := false
  333. lines := map[string]bool{} // already found lines
  334. for _, l := range strings.Split(input, "\n") {
  335. isStackLine := stackLineR.MatchString(l)
  336. switch {
  337. case !isStackLine && wasStack:
  338. blocks = append(blocks, stack, l)
  339. stack = ""
  340. lines = map[string]bool{}
  341. case isStackLine:
  342. if wasStack {
  343. // Detecting two stacks after another, possible cause lines match in
  344. // our tests due to WithStack(WithStack(io.EOF)) on same line.
  345. if lines[l] {
  346. if len(stack) == 0 {
  347. return nil, errors.New("len of block must not be zero here")
  348. }
  349. blocks = append(blocks, stack)
  350. stack = l
  351. lines = map[string]bool{l: true}
  352. continue
  353. }
  354. stack = stack + "\n" + l
  355. } else {
  356. stack = l
  357. }
  358. lines[l] = true
  359. case !isStackLine && !wasStack:
  360. blocks = append(blocks, l)
  361. default:
  362. return nil, errors.New("must not happen")
  363. }
  364. wasStack = isStackLine
  365. }
  366. // Use up stack
  367. if stack != "" {
  368. blocks = append(blocks, stack)
  369. }
  370. return blocks, nil
  371. }
  372. func testFormatCompleteCompare(t *testing.T, n int, arg interface{}, format string, want []string) {
  373. gotStr := fmt.Sprintf(format, arg)
  374. got, err := parseBlocks(gotStr)
  375. if err != nil {
  376. t.Fatal(err)
  377. }
  378. if len(got) != len(want) {
  379. t.Fatalf("test %d: fmt.Sprintf(%s, err) -> wrong number of blocks: got(%d) want(%d)\n got: %q\nwant: %q\ngotStr: %q",
  380. n+1, format, len(got), len(want), got, want, gotStr)
  381. }
  382. for i := range got {
  383. if strings.ContainsAny(want[i], "\n") {
  384. // Match as stack
  385. match, err := regexp.MatchString(want[i], got[i])
  386. if err != nil {
  387. t.Fatal(err)
  388. }
  389. if !match {
  390. t.Errorf("test %d: block %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, got[i], want[i])
  391. }
  392. } else {
  393. // Match as message
  394. if got[i] != want[i] {
  395. t.Errorf("test %d: fmt.Sprintf(%s, err) at block %d got != want:\n got: %q\nwant: %q", n+1, format, i+1, got[i], want[i])
  396. }
  397. }
  398. }
  399. }