OSDN Git Service

Hulk did something
[bytom/vapor.git] / vendor / github.com / pkg / errors / format_test.go
1 package errors
2
3 import (
4         "errors"
5         "fmt"
6         "io"
7         "regexp"
8         "strings"
9         "testing"
10 )
11
12 func TestFormatNew(t *testing.T) {
13         tests := []struct {
14                 error
15                 format string
16                 want   string
17         }{{
18                 New("error"),
19                 "%s",
20                 "error",
21         }, {
22                 New("error"),
23                 "%v",
24                 "error",
25         }, {
26                 New("error"),
27                 "%+v",
28                 "error\n" +
29                         "github.com/pkg/errors.TestFormatNew\n" +
30                         "\t.+/github.com/pkg/errors/format_test.go:26",
31         }, {
32                 New("error"),
33                 "%q",
34                 `"error"`,
35         }}
36
37         for i, tt := range tests {
38                 testFormatRegexp(t, i, tt.error, tt.format, tt.want)
39         }
40 }
41
42 func TestFormatErrorf(t *testing.T) {
43         tests := []struct {
44                 error
45                 format string
46                 want   string
47         }{{
48                 Errorf("%s", "error"),
49                 "%s",
50                 "error",
51         }, {
52                 Errorf("%s", "error"),
53                 "%v",
54                 "error",
55         }, {
56                 Errorf("%s", "error"),
57                 "%+v",
58                 "error\n" +
59                         "github.com/pkg/errors.TestFormatErrorf\n" +
60                         "\t.+/github.com/pkg/errors/format_test.go:56",
61         }}
62
63         for i, tt := range tests {
64                 testFormatRegexp(t, i, tt.error, tt.format, tt.want)
65         }
66 }
67
68 func TestFormatWrap(t *testing.T) {
69         tests := []struct {
70                 error
71                 format string
72                 want   string
73         }{{
74                 Wrap(New("error"), "error2"),
75                 "%s",
76                 "error2: error",
77         }, {
78                 Wrap(New("error"), "error2"),
79                 "%v",
80                 "error2: error",
81         }, {
82                 Wrap(New("error"), "error2"),
83                 "%+v",
84                 "error\n" +
85                         "github.com/pkg/errors.TestFormatWrap\n" +
86                         "\t.+/github.com/pkg/errors/format_test.go:82",
87         }, {
88                 Wrap(io.EOF, "error"),
89                 "%s",
90                 "error: EOF",
91         }, {
92                 Wrap(io.EOF, "error"),
93                 "%v",
94                 "error: EOF",
95         }, {
96                 Wrap(io.EOF, "error"),
97                 "%+v",
98                 "EOF\n" +
99                         "error\n" +
100                         "github.com/pkg/errors.TestFormatWrap\n" +
101                         "\t.+/github.com/pkg/errors/format_test.go:96",
102         }, {
103                 Wrap(Wrap(io.EOF, "error1"), "error2"),
104                 "%+v",
105                 "EOF\n" +
106                         "error1\n" +
107                         "github.com/pkg/errors.TestFormatWrap\n" +
108                         "\t.+/github.com/pkg/errors/format_test.go:103\n",
109         }, {
110                 Wrap(New("error with space"), "context"),
111                 "%q",
112                 `"context: error with space"`,
113         }}
114
115         for i, tt := range tests {
116                 testFormatRegexp(t, i, tt.error, tt.format, tt.want)
117         }
118 }
119
120 func TestFormatWrapf(t *testing.T) {
121         tests := []struct {
122                 error
123                 format string
124                 want   string
125         }{{
126                 Wrapf(io.EOF, "error%d", 2),
127                 "%s",
128                 "error2: EOF",
129         }, {
130                 Wrapf(io.EOF, "error%d", 2),
131                 "%v",
132                 "error2: EOF",
133         }, {
134                 Wrapf(io.EOF, "error%d", 2),
135                 "%+v",
136                 "EOF\n" +
137                         "error2\n" +
138                         "github.com/pkg/errors.TestFormatWrapf\n" +
139                         "\t.+/github.com/pkg/errors/format_test.go:134",
140         }, {
141                 Wrapf(New("error"), "error%d", 2),
142                 "%s",
143                 "error2: error",
144         }, {
145                 Wrapf(New("error"), "error%d", 2),
146                 "%v",
147                 "error2: error",
148         }, {
149                 Wrapf(New("error"), "error%d", 2),
150                 "%+v",
151                 "error\n" +
152                         "github.com/pkg/errors.TestFormatWrapf\n" +
153                         "\t.+/github.com/pkg/errors/format_test.go:149",
154         }}
155
156         for i, tt := range tests {
157                 testFormatRegexp(t, i, tt.error, tt.format, tt.want)
158         }
159 }
160
161 func TestFormatWithStack(t *testing.T) {
162         tests := []struct {
163                 error
164                 format string
165                 want   []string
166         }{{
167                 WithStack(io.EOF),
168                 "%s",
169                 []string{"EOF"},
170         }, {
171                 WithStack(io.EOF),
172                 "%v",
173                 []string{"EOF"},
174         }, {
175                 WithStack(io.EOF),
176                 "%+v",
177                 []string{"EOF",
178                         "github.com/pkg/errors.TestFormatWithStack\n" +
179                                 "\t.+/github.com/pkg/errors/format_test.go:175"},
180         }, {
181                 WithStack(New("error")),
182                 "%s",
183                 []string{"error"},
184         }, {
185                 WithStack(New("error")),
186                 "%v",
187                 []string{"error"},
188         }, {
189                 WithStack(New("error")),
190                 "%+v",
191                 []string{"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"},
196         }, {
197                 WithStack(WithStack(io.EOF)),
198                 "%+v",
199                 []string{"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"},
204         }, {
205                 WithStack(WithStack(Wrapf(io.EOF, "message"))),
206                 "%+v",
207                 []string{"EOF",
208                         "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"},
215         }, {
216                 WithStack(Errorf("error%d", 1)),
217                 "%+v",
218                 []string{"error1",
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"},
223         }}
224
225         for i, tt := range tests {
226                 testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true)
227         }
228 }
229
230 func TestFormatWithMessage(t *testing.T) {
231         tests := []struct {
232                 error
233                 format string
234                 want   []string
235         }{{
236                 WithMessage(New("error"), "error2"),
237                 "%s",
238                 []string{"error2: error"},
239         }, {
240                 WithMessage(New("error"), "error2"),
241                 "%v",
242                 []string{"error2: error"},
243         }, {
244                 WithMessage(New("error"), "error2"),
245                 "%+v",
246                 []string{
247                         "error",
248                         "github.com/pkg/errors.TestFormatWithMessage\n" +
249                                 "\t.+/github.com/pkg/errors/format_test.go:244",
250                         "error2"},
251         }, {
252                 WithMessage(io.EOF, "addition1"),
253                 "%s",
254                 []string{"addition1: EOF"},
255         }, {
256                 WithMessage(io.EOF, "addition1"),
257                 "%v",
258                 []string{"addition1: EOF"},
259         }, {
260                 WithMessage(io.EOF, "addition1"),
261                 "%+v",
262                 []string{"EOF", "addition1"},
263         }, {
264                 WithMessage(WithMessage(io.EOF, "addition1"), "addition2"),
265                 "%v",
266                 []string{"addition2: addition1: EOF"},
267         }, {
268                 WithMessage(WithMessage(io.EOF, "addition1"), "addition2"),
269                 "%+v",
270                 []string{"EOF", "addition1", "addition2"},
271         }, {
272                 Wrap(WithMessage(io.EOF, "error1"), "error2"),
273                 "%+v",
274                 []string{"EOF", "error1", "error2",
275                         "github.com/pkg/errors.TestFormatWithMessage\n" +
276                                 "\t.+/github.com/pkg/errors/format_test.go:272"},
277         }, {
278                 WithMessage(Errorf("error%d", 1), "error2"),
279                 "%+v",
280                 []string{"error1",
281                         "github.com/pkg/errors.TestFormatWithMessage\n" +
282                                 "\t.+/github.com/pkg/errors/format_test.go:278",
283                         "error2"},
284         }, {
285                 WithMessage(WithStack(io.EOF), "error"),
286                 "%+v",
287                 []string{
288                         "EOF",
289                         "github.com/pkg/errors.TestFormatWithMessage\n" +
290                                 "\t.+/github.com/pkg/errors/format_test.go:285",
291                         "error"},
292         }, {
293                 WithMessage(Wrap(WithStack(io.EOF), "inside-error"), "outside-error"),
294                 "%+v",
295                 []string{
296                         "EOF",
297                         "github.com/pkg/errors.TestFormatWithMessage\n" +
298                                 "\t.+/github.com/pkg/errors/format_test.go:293",
299                         "inside-error",
300                         "github.com/pkg/errors.TestFormatWithMessage\n" +
301                                 "\t.+/github.com/pkg/errors/format_test.go:293",
302                         "outside-error"},
303         }}
304
305         for i, tt := range tests {
306                 testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true)
307         }
308 }
309
310 func TestFormatGeneric(t *testing.T) {
311         starts := []struct {
312                 err  error
313                 want []string
314         }{
315                 {New("new-error"), []string{
316                         "new-error",
317                         "github.com/pkg/errors.TestFormatGeneric\n" +
318                                 "\t.+/github.com/pkg/errors/format_test.go:315"},
319                 }, {Errorf("errorf-error"), []string{
320                         "errorf-error",
321                         "github.com/pkg/errors.TestFormatGeneric\n" +
322                                 "\t.+/github.com/pkg/errors/format_test.go:319"},
323                 }, {errors.New("errors-new-error"), []string{
324                         "errors-new-error"},
325                 },
326         }
327
328         wrappers := []wrapper{
329                 {
330                         func(err error) error { return WithMessage(err, "with-message") },
331                         []string{"with-message"},
332                 }, {
333                         func(err error) error { return WithStack(err) },
334                         []string{
335                                 "github.com/pkg/errors.(func·002|TestFormatGeneric.func2)\n\t" +
336                                         ".+/github.com/pkg/errors/format_test.go:333",
337                         },
338                 }, {
339                         func(err error) error { return Wrap(err, "wrap-error") },
340                         []string{
341                                 "wrap-error",
342                                 "github.com/pkg/errors.(func·003|TestFormatGeneric.func3)\n\t" +
343                                         ".+/github.com/pkg/errors/format_test.go:339",
344                         },
345                 }, {
346                         func(err error) error { return Wrapf(err, "wrapf-error%d", 1) },
347                         []string{
348                                 "wrapf-error1",
349                                 "github.com/pkg/errors.(func·004|TestFormatGeneric.func4)\n\t" +
350                                         ".+/github.com/pkg/errors/format_test.go:346",
351                         },
352                 },
353         }
354
355         for s := range starts {
356                 err := starts[s].err
357                 want := starts[s].want
358                 testFormatCompleteCompare(t, s, err, "%+v", want, false)
359                 testGenericRecursive(t, err, want, wrappers, 3)
360         }
361 }
362
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)
367
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)
370                 return
371         }
372
373         for i, w := range wantLines {
374                 match, err := regexp.MatchString(w, gotLines[i])
375                 if err != nil {
376                         t.Fatal(err)
377                 }
378                 if !match {
379                         t.Errorf("test %d: line %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, got, want)
380                 }
381         }
382 }
383
384 var stackLineR = regexp.MustCompile(`\.`)
385
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.
389 //
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.
392 //
393 // Example use:
394 //
395 // for _, e := range blocks {
396 //   if strings.ContainsAny(e, "\n") {
397 //     // Match as stack
398 //   } else {
399 //     // Match as line
400 //   }
401 // }
402 //
403 func parseBlocks(input string, detectStackboundaries bool) ([]string, error) {
404         var blocks []string
405
406         stack := ""
407         wasStack := false
408         lines := map[string]bool{} // already found lines
409
410         for _, l := range strings.Split(input, "\n") {
411                 isStackLine := stackLineR.MatchString(l)
412
413                 switch {
414                 case !isStackLine && wasStack:
415                         blocks = append(blocks, stack, l)
416                         stack = ""
417                         lines = map[string]bool{}
418                 case isStackLine:
419                         if wasStack {
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 {
423                                         if lines[l] {
424                                                 if len(stack) == 0 {
425                                                         return nil, errors.New("len of block must not be zero here")
426                                                 }
427
428                                                 blocks = append(blocks, stack)
429                                                 stack = l
430                                                 lines = map[string]bool{l: true}
431                                                 continue
432                                         }
433                                 }
434
435                                 stack = stack + "\n" + l
436                         } else {
437                                 stack = l
438                         }
439                         lines[l] = true
440                 case !isStackLine && !wasStack:
441                         blocks = append(blocks, l)
442                 default:
443                         return nil, errors.New("must not happen")
444                 }
445
446                 wasStack = isStackLine
447         }
448
449         // Use up stack
450         if stack != "" {
451                 blocks = append(blocks, stack)
452         }
453         return blocks, nil
454 }
455
456 func testFormatCompleteCompare(t *testing.T, n int, arg interface{}, format string, want []string, detectStackBoundaries bool) {
457         gotStr := fmt.Sprintf(format, arg)
458
459         got, err := parseBlocks(gotStr, detectStackBoundaries)
460         if err != nil {
461                 t.Fatal(err)
462         }
463
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)
467         }
468
469         for i := range got {
470                 if strings.ContainsAny(want[i], "\n") {
471                         // Match as stack
472                         match, err := regexp.MatchString(want[i], got[i])
473                         if err != nil {
474                                 t.Fatal(err)
475                         }
476                         if !match {
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))
479                         }
480                 } else {
481                         // Match as message
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])
484                         }
485                 }
486         }
487 }
488
489 type wrapper struct {
490         wrap func(err error) error
491         want []string
492 }
493
494 func prettyBlocks(blocks []string) string {
495         var out []string
496
497         for _, b := range blocks {
498                 out = append(out, fmt.Sprintf("%v", b))
499         }
500
501         return "   " + strings.Join(out, "\n   ")
502 }
503
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")
507         }
508         for _, w := range list {
509                 if len(w.want) == 0 {
510                         panic("want must not be empty")
511                 }
512
513                 err := w.wrap(beforeErr)
514
515                 // Copy required cause append(beforeWant, ..) modified beforeWant subtly.
516                 beforeCopy := make([]string, len(beforeWant))
517                 copy(beforeCopy, beforeWant)
518
519                 beforeWant := beforeCopy
520                 last := len(beforeWant) - 1
521                 var want []string
522
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:]...)...)
526                 } else {
527                         want = append(beforeWant, w.want...)
528                 }
529
530                 testFormatCompleteCompare(t, maxDepth, err, "%+v", want, false)
531                 if maxDepth > 0 {
532                         testGenericRecursive(t, err, want, list, maxDepth-1)
533                 }
534         }
535 }