12 func TestFormatNew(t *testing.T) {
29 "github.com/pkg/errors.TestFormatNew\n" +
30 "\t.+/github.com/pkg/errors/format_test.go:26",
37 for i, tt := range tests {
38 testFormatRegexp(t, i, tt.error, tt.format, tt.want)
42 func TestFormatErrorf(t *testing.T) {
48 Errorf("%s", "error"),
52 Errorf("%s", "error"),
56 Errorf("%s", "error"),
59 "github.com/pkg/errors.TestFormatErrorf\n" +
60 "\t.+/github.com/pkg/errors/format_test.go:56",
63 for i, tt := range tests {
64 testFormatRegexp(t, i, tt.error, tt.format, tt.want)
68 func TestFormatWrap(t *testing.T) {
74 Wrap(New("error"), "error2"),
78 Wrap(New("error"), "error2"),
82 Wrap(New("error"), "error2"),
85 "github.com/pkg/errors.TestFormatWrap\n" +
86 "\t.+/github.com/pkg/errors/format_test.go:82",
88 Wrap(io.EOF, "error"),
92 Wrap(io.EOF, "error"),
96 Wrap(io.EOF, "error"),
100 "github.com/pkg/errors.TestFormatWrap\n" +
101 "\t.+/github.com/pkg/errors/format_test.go:96",
103 Wrap(Wrap(io.EOF, "error1"), "error2"),
107 "github.com/pkg/errors.TestFormatWrap\n" +
108 "\t.+/github.com/pkg/errors/format_test.go:103\n",
110 Wrap(New("error with space"), "context"),
112 `"context: error with space"`,
115 for i, tt := range tests {
116 testFormatRegexp(t, i, tt.error, tt.format, tt.want)
120 func TestFormatWrapf(t *testing.T) {
126 Wrapf(io.EOF, "error%d", 2),
130 Wrapf(io.EOF, "error%d", 2),
134 Wrapf(io.EOF, "error%d", 2),
138 "github.com/pkg/errors.TestFormatWrapf\n" +
139 "\t.+/github.com/pkg/errors/format_test.go:134",
141 Wrapf(New("error"), "error%d", 2),
145 Wrapf(New("error"), "error%d", 2),
149 Wrapf(New("error"), "error%d", 2),
152 "github.com/pkg/errors.TestFormatWrapf\n" +
153 "\t.+/github.com/pkg/errors/format_test.go:149",
156 for i, tt := range tests {
157 testFormatRegexp(t, i, tt.error, tt.format, tt.want)
161 func TestFormatWithStack(t *testing.T) {
178 "github.com/pkg/errors.TestFormatWithStack\n" +
179 "\t.+/github.com/pkg/errors/format_test.go:175"},
181 WithStack(New("error")),
185 WithStack(New("error")),
189 WithStack(New("error")),
192 "github.com/pkg/errors.TestFormatWithStack\n" +
193 "\t.+/github.com/pkg/errors/format_test.go:189",
194 "github.com/pkg/errors.TestFormatWithStack\n" +
195 "\t.+/github.com/pkg/errors/format_test.go:189"},
197 WithStack(WithStack(io.EOF)),
200 "github.com/pkg/errors.TestFormatWithStack\n" +
201 "\t.+/github.com/pkg/errors/format_test.go:197",
202 "github.com/pkg/errors.TestFormatWithStack\n" +
203 "\t.+/github.com/pkg/errors/format_test.go:197"},
205 WithStack(WithStack(Wrapf(io.EOF, "message"))),
209 "github.com/pkg/errors.TestFormatWithStack\n" +
210 "\t.+/github.com/pkg/errors/format_test.go:205",
211 "github.com/pkg/errors.TestFormatWithStack\n" +
212 "\t.+/github.com/pkg/errors/format_test.go:205",
213 "github.com/pkg/errors.TestFormatWithStack\n" +
214 "\t.+/github.com/pkg/errors/format_test.go:205"},
216 WithStack(Errorf("error%d", 1)),
219 "github.com/pkg/errors.TestFormatWithStack\n" +
220 "\t.+/github.com/pkg/errors/format_test.go:216",
221 "github.com/pkg/errors.TestFormatWithStack\n" +
222 "\t.+/github.com/pkg/errors/format_test.go:216"},
225 for i, tt := range tests {
226 testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true)
230 func TestFormatWithMessage(t *testing.T) {
236 WithMessage(New("error"), "error2"),
238 []string{"error2: error"},
240 WithMessage(New("error"), "error2"),
242 []string{"error2: error"},
244 WithMessage(New("error"), "error2"),
248 "github.com/pkg/errors.TestFormatWithMessage\n" +
249 "\t.+/github.com/pkg/errors/format_test.go:244",
252 WithMessage(io.EOF, "addition1"),
254 []string{"addition1: EOF"},
256 WithMessage(io.EOF, "addition1"),
258 []string{"addition1: EOF"},
260 WithMessage(io.EOF, "addition1"),
262 []string{"EOF", "addition1"},
264 WithMessage(WithMessage(io.EOF, "addition1"), "addition2"),
266 []string{"addition2: addition1: EOF"},
268 WithMessage(WithMessage(io.EOF, "addition1"), "addition2"),
270 []string{"EOF", "addition1", "addition2"},
272 Wrap(WithMessage(io.EOF, "error1"), "error2"),
274 []string{"EOF", "error1", "error2",
275 "github.com/pkg/errors.TestFormatWithMessage\n" +
276 "\t.+/github.com/pkg/errors/format_test.go:272"},
278 WithMessage(Errorf("error%d", 1), "error2"),
281 "github.com/pkg/errors.TestFormatWithMessage\n" +
282 "\t.+/github.com/pkg/errors/format_test.go:278",
285 WithMessage(WithStack(io.EOF), "error"),
289 "github.com/pkg/errors.TestFormatWithMessage\n" +
290 "\t.+/github.com/pkg/errors/format_test.go:285",
293 WithMessage(Wrap(WithStack(io.EOF), "inside-error"), "outside-error"),
297 "github.com/pkg/errors.TestFormatWithMessage\n" +
298 "\t.+/github.com/pkg/errors/format_test.go:293",
300 "github.com/pkg/errors.TestFormatWithMessage\n" +
301 "\t.+/github.com/pkg/errors/format_test.go:293",
305 for i, tt := range tests {
306 testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true)
310 func TestFormatGeneric(t *testing.T) {
315 {New("new-error"), []string{
317 "github.com/pkg/errors.TestFormatGeneric\n" +
318 "\t.+/github.com/pkg/errors/format_test.go:315"},
319 }, {Errorf("errorf-error"), []string{
321 "github.com/pkg/errors.TestFormatGeneric\n" +
322 "\t.+/github.com/pkg/errors/format_test.go:319"},
323 }, {errors.New("errors-new-error"), []string{
328 wrappers := []wrapper{
330 func(err error) error { return WithMessage(err, "with-message") },
331 []string{"with-message"},
333 func(err error) error { return WithStack(err) },
335 "github.com/pkg/errors.(func·002|TestFormatGeneric.func2)\n\t" +
336 ".+/github.com/pkg/errors/format_test.go:333",
339 func(err error) error { return Wrap(err, "wrap-error") },
342 "github.com/pkg/errors.(func·003|TestFormatGeneric.func3)\n\t" +
343 ".+/github.com/pkg/errors/format_test.go:339",
346 func(err error) error { return Wrapf(err, "wrapf-error%d", 1) },
349 "github.com/pkg/errors.(func·004|TestFormatGeneric.func4)\n\t" +
350 ".+/github.com/pkg/errors/format_test.go:346",
355 for s := range starts {
357 want := starts[s].want
358 testFormatCompleteCompare(t, s, err, "%+v", want, false)
359 testGenericRecursive(t, err, want, wrappers, 3)
363 func testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) {
364 got := fmt.Sprintf(format, arg)
365 gotLines := strings.SplitN(got, "\n", -1)
366 wantLines := strings.SplitN(want, "\n", -1)
368 if len(wantLines) > len(gotLines) {
369 t.Errorf("test %d: wantLines(%d) > gotLines(%d):\n got: %q\nwant: %q", n+1, len(wantLines), len(gotLines), got, want)
373 for i, w := range wantLines {
374 match, err := regexp.MatchString(w, gotLines[i])
379 t.Errorf("test %d: line %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, got, want)
384 var stackLineR = regexp.MustCompile(`\.`)
386 // parseBlocks parses input into a slice, where:
387 // - incase entry contains a newline, its a stacktrace
388 // - incase entry contains no newline, its a solo line.
390 // Detecting stack boundaries only works incase the WithStack-calls are
391 // to be found on the same line, thats why it is optionally here.
395 // for _, e := range blocks {
396 // if strings.ContainsAny(e, "\n") {
403 func parseBlocks(input string, detectStackboundaries bool) ([]string, error) {
408 lines := map[string]bool{} // already found lines
410 for _, l := range strings.Split(input, "\n") {
411 isStackLine := stackLineR.MatchString(l)
414 case !isStackLine && wasStack:
415 blocks = append(blocks, stack, l)
417 lines = map[string]bool{}
420 // Detecting two stacks after another, possible cause lines match in
421 // our tests due to WithStack(WithStack(io.EOF)) on same line.
422 if detectStackboundaries {
425 return nil, errors.New("len of block must not be zero here")
428 blocks = append(blocks, stack)
430 lines = map[string]bool{l: true}
435 stack = stack + "\n" + l
440 case !isStackLine && !wasStack:
441 blocks = append(blocks, l)
443 return nil, errors.New("must not happen")
446 wasStack = isStackLine
451 blocks = append(blocks, stack)
456 func testFormatCompleteCompare(t *testing.T, n int, arg interface{}, format string, want []string, detectStackBoundaries bool) {
457 gotStr := fmt.Sprintf(format, arg)
459 got, err := parseBlocks(gotStr, detectStackBoundaries)
464 if len(got) != len(want) {
465 t.Fatalf("test %d: fmt.Sprintf(%s, err) -> wrong number of blocks: got(%d) want(%d)\n got: %s\nwant: %s\ngotStr: %q",
466 n+1, format, len(got), len(want), prettyBlocks(got), prettyBlocks(want), gotStr)
470 if strings.ContainsAny(want[i], "\n") {
472 match, err := regexp.MatchString(want[i], got[i])
477 t.Fatalf("test %d: block %d: fmt.Sprintf(%q, err):\ngot:\n%q\nwant:\n%q\nall-got:\n%s\nall-want:\n%s\n",
478 n+1, i+1, format, got[i], want[i], prettyBlocks(got), prettyBlocks(want))
482 if got[i] != want[i] {
483 t.Fatalf("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])
489 type wrapper struct {
490 wrap func(err error) error
494 func prettyBlocks(blocks []string) string {
497 for _, b := range blocks {
498 out = append(out, fmt.Sprintf("%v", b))
501 return " " + strings.Join(out, "\n ")
504 func testGenericRecursive(t *testing.T, beforeErr error, beforeWant []string, list []wrapper, maxDepth int) {
505 if len(beforeWant) == 0 {
506 panic("beforeWant must not be empty")
508 for _, w := range list {
509 if len(w.want) == 0 {
510 panic("want must not be empty")
513 err := w.wrap(beforeErr)
515 // Copy required cause append(beforeWant, ..) modified beforeWant subtly.
516 beforeCopy := make([]string, len(beforeWant))
517 copy(beforeCopy, beforeWant)
519 beforeWant := beforeCopy
520 last := len(beforeWant) - 1
523 // Merge two stacks behind each other.
524 if strings.ContainsAny(beforeWant[last], "\n") && strings.ContainsAny(w.want[0], "\n") {
525 want = append(beforeWant[:last], append([]string{beforeWant[last] + "((?s).*)" + w.want[0]}, w.want[1:]...)...)
527 want = append(beforeWant, w.want...)
530 testFormatCompleteCompare(t, maxDepth, err, "%+v", want, false)
532 testGenericRecursive(t, err, want, list, maxDepth-1)