Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / xerrors@v0.0.0-20200804184101-5ec99f83aff1 / fmt_test.go
1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package xerrors_test
6
7 import (
8         "fmt"
9         "io"
10         "os"
11         "path"
12         "reflect"
13         "regexp"
14         "strconv"
15         "strings"
16         "testing"
17
18         "golang.org/x/xerrors"
19 )
20
21 func TestErrorf(t *testing.T) {
22         chained := &wrapped{"chained", nil}
23         chain := func(s ...string) (a []string) {
24                 for _, s := range s {
25                         a = append(a, cleanPath(s))
26                 }
27                 return a
28         }
29         testCases := []struct {
30                 got  error
31                 want []string
32         }{{
33                 xerrors.Errorf("no args"),
34                 chain("no args/path.TestErrorf/path.go:xxx"),
35         }, {
36                 xerrors.Errorf("no args: %s"),
37                 chain("no args: %!s(MISSING)/path.TestErrorf/path.go:xxx"),
38         }, {
39                 xerrors.Errorf("nounwrap: %s", "simple"),
40                 chain(`nounwrap: simple/path.TestErrorf/path.go:xxx`),
41         }, {
42                 xerrors.Errorf("nounwrap: %v", "simple"),
43                 chain(`nounwrap: simple/path.TestErrorf/path.go:xxx`),
44         }, {
45                 xerrors.Errorf("%s failed: %v", "foo", chained),
46                 chain("foo failed/path.TestErrorf/path.go:xxx",
47                         "chained/somefile.go:xxx"),
48         }, {
49                 xerrors.Errorf("no wrap: %s", chained),
50                 chain("no wrap/path.TestErrorf/path.go:xxx",
51                         "chained/somefile.go:xxx"),
52         }, {
53                 xerrors.Errorf("%s failed: %w", "foo", chained),
54                 chain("wraps:foo failed/path.TestErrorf/path.go:xxx",
55                         "chained/somefile.go:xxx"),
56         }, {
57                 xerrors.Errorf("nowrapv: %v", chained),
58                 chain("nowrapv/path.TestErrorf/path.go:xxx",
59                         "chained/somefile.go:xxx"),
60         }, {
61                 xerrors.Errorf("wrapw: %w", chained),
62                 chain("wraps:wrapw/path.TestErrorf/path.go:xxx",
63                         "chained/somefile.go:xxx"),
64         }, {
65                 xerrors.Errorf("wrapw %w middle", chained),
66                 chain("wraps:wrapw chained middle/path.TestErrorf/path.go:xxx",
67                         "chained/somefile.go:xxx"),
68         }, {
69                 xerrors.Errorf("not wrapped: %+v", chained),
70                 chain("not wrapped: chained: somefile.go:123/path.TestErrorf/path.go:xxx"),
71         }}
72         for i, tc := range testCases {
73                 t.Run(strconv.Itoa(i)+"/"+path.Join(tc.want...), func(t *testing.T) {
74                         got := errToParts(tc.got)
75                         if !reflect.DeepEqual(got, tc.want) {
76                                 t.Errorf("Format:\n got: %#v\nwant: %#v", got, tc.want)
77                         }
78
79                         gotStr := tc.got.Error()
80                         wantStr := fmt.Sprint(tc.got)
81                         if gotStr != wantStr {
82                                 t.Errorf("Error:\n got: %#v\nwant: %#v", got, tc.want)
83                         }
84                 })
85         }
86 }
87
88 func TestErrorFormatter(t *testing.T) {
89         var (
90                 simple   = &wrapped{"simple", nil}
91                 elephant = &wrapped{
92                         "can't adumbrate elephant",
93                         detailed{},
94                 }
95                 nonascii = &wrapped{"cafĂ©", nil}
96                 newline  = &wrapped{"msg with\nnewline",
97                         &wrapped{"and another\none", nil}}
98                 fallback  = &wrapped{"fallback", os.ErrNotExist}
99                 oldAndNew = &wrapped{"new style", formatError("old style")}
100                 framed    = &withFrameAndMore{
101                         frame: xerrors.Caller(0),
102                 }
103                 opaque = &wrapped{"outer",
104                         xerrors.Opaque(&wrapped{"mid",
105                                 &wrapped{"inner", nil}})}
106         )
107         testCases := []struct {
108                 err    error
109                 fmt    string
110                 want   string
111                 regexp bool
112         }{{
113                 err:  simple,
114                 fmt:  "%s",
115                 want: "simple",
116         }, {
117                 err:  elephant,
118                 fmt:  "%s",
119                 want: "can't adumbrate elephant: out of peanuts",
120         }, {
121                 err:  &wrapped{"a", &wrapped{"b", &wrapped{"c", nil}}},
122                 fmt:  "%s",
123                 want: "a: b: c",
124         }, {
125                 err: simple,
126                 fmt: "%+v",
127                 want: "simple:" +
128                         "\n    somefile.go:123",
129         }, {
130                 err: elephant,
131                 fmt: "%+v",
132                 want: "can't adumbrate elephant:" +
133                         "\n    somefile.go:123" +
134                         "\n  - out of peanuts:" +
135                         "\n    the elephant is on strike" +
136                         "\n    and the 12 monkeys" +
137                         "\n    are laughing",
138         }, {
139                 err:  &oneNewline{nil},
140                 fmt:  "%+v",
141                 want: "123",
142         }, {
143                 err: &oneNewline{&oneNewline{nil}},
144                 fmt: "%+v",
145                 want: "123:" +
146                         "\n  - 123",
147         }, {
148                 err:  &newlineAtEnd{nil},
149                 fmt:  "%+v",
150                 want: "newlineAtEnd:\n    detail",
151         }, {
152                 err: &newlineAtEnd{&newlineAtEnd{nil}},
153                 fmt: "%+v",
154                 want: "newlineAtEnd:" +
155                         "\n    detail" +
156                         "\n  - newlineAtEnd:" +
157                         "\n    detail",
158         }, {
159                 err: framed,
160                 fmt: "%+v",
161                 want: "something:" +
162                         "\n    golang.org/x/xerrors_test.TestErrorFormatter" +
163                         "\n        .+/fmt_test.go:101" +
164                         "\n    something more",
165                 regexp: true,
166         }, {
167                 err:  fmtTwice("Hello World!"),
168                 fmt:  "%#v",
169                 want: "2 times Hello World!",
170         }, {
171                 err:  fallback,
172                 fmt:  "%s",
173                 want: "fallback: file does not exist",
174         }, {
175                 err: fallback,
176                 fmt: "%+v",
177                 // Note: no colon after the last error, as there are no details.
178                 want: "fallback:" +
179                         "\n    somefile.go:123" +
180                         "\n  - file does not exist",
181         }, {
182                 err:  opaque,
183                 fmt:  "%s",
184                 want: "outer: mid: inner",
185         }, {
186                 err: opaque,
187                 fmt: "%+v",
188                 want: "outer:" +
189                         "\n    somefile.go:123" +
190                         "\n  - mid:" +
191                         "\n    somefile.go:123" +
192                         "\n  - inner:" +
193                         "\n    somefile.go:123",
194         }, {
195                 err:  oldAndNew,
196                 fmt:  "%v",
197                 want: "new style: old style",
198         }, {
199                 err:  oldAndNew,
200                 fmt:  "%q",
201                 want: `"new style: old style"`,
202         }, {
203                 err: oldAndNew,
204                 fmt: "%+v",
205                 // Note the extra indentation.
206                 // Colon for old style error is rendered by the fmt.Formatter
207                 // implementation of the old-style error.
208                 want: "new style:" +
209                         "\n    somefile.go:123" +
210                         "\n  - old style:" +
211                         "\n    otherfile.go:456",
212         }, {
213                 err:  simple,
214                 fmt:  "%-12s",
215                 want: "simple      ",
216         }, {
217                 // Don't use formatting flags for detailed view.
218                 err: simple,
219                 fmt: "%+12v",
220                 want: "simple:" +
221                         "\n    somefile.go:123",
222         }, {
223                 err:  elephant,
224                 fmt:  "%+50s",
225                 want: "          can't adumbrate elephant: out of peanuts",
226         }, {
227                 err:  nonascii,
228                 fmt:  "%q",
229                 want: `"cafĂ©"`,
230         }, {
231                 err:  nonascii,
232                 fmt:  "%+q",
233                 want: `"caf\u00e9"`,
234         }, {
235                 err:  simple,
236                 fmt:  "% x",
237                 want: "73 69 6d 70 6c 65",
238         }, {
239                 err: newline,
240                 fmt: "%s",
241                 want: "msg with" +
242                         "\nnewline: and another" +
243                         "\none",
244         }, {
245                 err: newline,
246                 fmt: "%+v",
247                 want: "msg with" +
248                         "\n    newline:" +
249                         "\n    somefile.go:123" +
250                         "\n  - and another" +
251                         "\n    one:" +
252                         "\n    somefile.go:123",
253         }, {
254                 err: &wrapped{"", &wrapped{"inner message", nil}},
255                 fmt: "%+v",
256                 want: "somefile.go:123" +
257                         "\n  - inner message:" +
258                         "\n    somefile.go:123",
259         }, {
260                 err:  spurious(""),
261                 fmt:  "%s",
262                 want: "spurious",
263         }, {
264                 err:  spurious(""),
265                 fmt:  "%+v",
266                 want: "spurious",
267         }, {
268                 err:  spurious("extra"),
269                 fmt:  "%s",
270                 want: "spurious",
271         }, {
272                 err: spurious("extra"),
273                 fmt: "%+v",
274                 want: "spurious:\n" +
275                         "    extra",
276         }, {
277                 err:  nil,
278                 fmt:  "%+v",
279                 want: "<nil>",
280         }, {
281                 err:  (*wrapped)(nil),
282                 fmt:  "%+v",
283                 want: "<nil>",
284         }, {
285                 err:  simple,
286                 fmt:  "%T",
287                 want: "*xerrors_test.wrapped",
288         }, {
289                 err:  simple,
290                 fmt:  "%🤪",
291                 want: "%!🤪(*xerrors_test.wrapped)",
292                 // For 1.13:
293                 //  want: "%!🤪(*xerrors_test.wrapped=&{simple <nil>})",
294         }, {
295                 err:  formatError("use fmt.Formatter"),
296                 fmt:  "%#v",
297                 want: "use fmt.Formatter",
298         }, {
299                 err: fmtTwice("%s %s", "ok", panicValue{}),
300                 fmt: "%s",
301                 // Different Go versions produce different results.
302                 want:   `ok %!s\(PANIC=(String method: )?panic\)/ok %!s\(PANIC=(String method: )?panic\)`,
303                 regexp: true,
304         }, {
305                 err:  fmtTwice("%o %s", panicValue{}, "ok"),
306                 fmt:  "%s",
307                 want: "{} ok/{} ok",
308         }, {
309                 err: adapted{"adapted", nil},
310                 fmt: "%+v",
311                 want: "adapted:" +
312                         "\n    detail",
313         }, {
314                 err: adapted{"outer", adapted{"mid", adapted{"inner", nil}}},
315                 fmt: "%+v",
316                 want: "outer:" +
317                         "\n    detail" +
318                         "\n  - mid:" +
319                         "\n    detail" +
320                         "\n  - inner:" +
321                         "\n    detail",
322         }}
323         for i, tc := range testCases {
324                 t.Run(fmt.Sprintf("%d/%s", i, tc.fmt), func(t *testing.T) {
325                         got := fmt.Sprintf(tc.fmt, tc.err)
326                         var ok bool
327                         if tc.regexp {
328                                 var err error
329                                 ok, err = regexp.MatchString(tc.want+"$", got)
330                                 if err != nil {
331                                         t.Fatal(err)
332                                 }
333                         } else {
334                                 ok = got == tc.want
335                         }
336                         if !ok {
337                                 t.Errorf("\n got: %q\nwant: %q", got, tc.want)
338                         }
339                 })
340         }
341 }
342
343 func TestAdaptor(t *testing.T) {
344         testCases := []struct {
345                 err    error
346                 fmt    string
347                 want   string
348                 regexp bool
349         }{{
350                 err: adapted{"adapted", nil},
351                 fmt: "%+v",
352                 want: "adapted:" +
353                         "\n    detail",
354         }, {
355                 err: adapted{"outer", adapted{"mid", adapted{"inner", nil}}},
356                 fmt: "%+v",
357                 want: "outer:" +
358                         "\n    detail" +
359                         "\n  - mid:" +
360                         "\n    detail" +
361                         "\n  - inner:" +
362                         "\n    detail",
363         }}
364         for i, tc := range testCases {
365                 t.Run(fmt.Sprintf("%d/%s", i, tc.fmt), func(t *testing.T) {
366                         got := fmt.Sprintf(tc.fmt, tc.err)
367                         if got != tc.want {
368                                 t.Errorf("\n got: %q\nwant: %q", got, tc.want)
369                         }
370                 })
371         }
372 }
373
374 var _ xerrors.Formatter = wrapped{}
375
376 type wrapped struct {
377         msg string
378         err error
379 }
380
381 func (e wrapped) Error() string { return "should call Format" }
382
383 func (e wrapped) Format(s fmt.State, verb rune) {
384         xerrors.FormatError(&e, s, verb)
385 }
386
387 func (e wrapped) FormatError(p xerrors.Printer) (next error) {
388         p.Print(e.msg)
389         p.Detail()
390         p.Print("somefile.go:123")
391         return e.err
392 }
393
394 var _ xerrors.Formatter = detailed{}
395
396 type detailed struct{}
397
398 func (e detailed) Error() string { panic("should have called FormatError") }
399
400 func (detailed) FormatError(p xerrors.Printer) (next error) {
401         p.Printf("out of %s", "peanuts")
402         p.Detail()
403         p.Print("the elephant is on strike\n")
404         p.Printf("and the %d monkeys\nare laughing", 12)
405         return nil
406 }
407
408 type withFrameAndMore struct {
409         frame xerrors.Frame
410 }
411
412 func (e *withFrameAndMore) Error() string { return fmt.Sprint(e) }
413
414 func (e *withFrameAndMore) Format(s fmt.State, v rune) {
415         xerrors.FormatError(e, s, v)
416 }
417
418 func (e *withFrameAndMore) FormatError(p xerrors.Printer) (next error) {
419         p.Print("something")
420         if p.Detail() {
421                 e.frame.Format(p)
422                 p.Print("something more")
423         }
424         return nil
425 }
426
427 type spurious string
428
429 func (e spurious) Error() string { return fmt.Sprint(e) }
430
431 // move to 1_12 test file
432 func (e spurious) Format(s fmt.State, verb rune) {
433         xerrors.FormatError(e, s, verb)
434 }
435
436 func (e spurious) FormatError(p xerrors.Printer) (next error) {
437         p.Print("spurious")
438         p.Detail() // Call detail even if we don't print anything
439         if e == "" {
440                 p.Print()
441         } else {
442                 p.Print("\n", string(e)) // print extraneous leading newline
443         }
444         return nil
445 }
446
447 type oneNewline struct {
448         next error
449 }
450
451 func (e *oneNewline) Error() string { return fmt.Sprint(e) }
452
453 func (e *oneNewline) Format(s fmt.State, verb rune) {
454         xerrors.FormatError(e, s, verb)
455 }
456
457 func (e *oneNewline) FormatError(p xerrors.Printer) (next error) {
458         p.Print("1")
459         p.Print("2")
460         p.Print("3")
461         p.Detail()
462         p.Print("\n")
463         return e.next
464 }
465
466 type newlineAtEnd struct {
467         next error
468 }
469
470 func (e *newlineAtEnd) Error() string { return fmt.Sprint(e) }
471
472 func (e *newlineAtEnd) Format(s fmt.State, verb rune) {
473         xerrors.FormatError(e, s, verb)
474 }
475
476 func (e *newlineAtEnd) FormatError(p xerrors.Printer) (next error) {
477         p.Print("newlineAtEnd")
478         p.Detail()
479         p.Print("detail\n")
480         return e.next
481 }
482
483 type adapted struct {
484         msg string
485         err error
486 }
487
488 func (e adapted) Error() string { return string(e.msg) }
489
490 func (e adapted) Format(s fmt.State, verb rune) {
491         xerrors.FormatError(e, s, verb)
492 }
493
494 func (e adapted) FormatError(p xerrors.Printer) error {
495         p.Print(e.msg)
496         p.Detail()
497         p.Print("detail")
498         return e.err
499 }
500
501 // formatError is an error implementing Format instead of xerrors.Formatter.
502 // The implementation mimics the implementation of github.com/pkg/errors.
503 type formatError string
504
505 func (e formatError) Error() string { return string(e) }
506
507 func (e formatError) Format(s fmt.State, verb rune) {
508         // Body based on pkg/errors/errors.go
509         switch verb {
510         case 'v':
511                 if s.Flag('+') {
512                         io.WriteString(s, string(e))
513                         fmt.Fprintf(s, ":\n%s", "otherfile.go:456")
514                         return
515                 }
516                 fallthrough
517         case 's':
518                 io.WriteString(s, string(e))
519         case 'q':
520                 fmt.Fprintf(s, "%q", string(e))
521         }
522 }
523
524 func (e formatError) GoString() string {
525         panic("should never be called")
526 }
527
528 type fmtTwiceErr struct {
529         format string
530         args   []interface{}
531 }
532
533 func fmtTwice(format string, a ...interface{}) error {
534         return fmtTwiceErr{format, a}
535 }
536
537 func (e fmtTwiceErr) Error() string { return fmt.Sprint(e) }
538
539 func (e fmtTwiceErr) Format(s fmt.State, verb rune) {
540         xerrors.FormatError(e, s, verb)
541 }
542
543 func (e fmtTwiceErr) FormatError(p xerrors.Printer) (next error) {
544         p.Printf(e.format, e.args...)
545         p.Print("/")
546         p.Printf(e.format, e.args...)
547         return nil
548 }
549
550 func (e fmtTwiceErr) GoString() string {
551         return "2 times " + fmt.Sprintf(e.format, e.args...)
552 }
553
554 type panicValue struct{}
555
556 func (panicValue) String() string { panic("panic") }
557
558 var rePath = regexp.MustCompile(`( [^ ]*)xerrors.*test\.`)
559 var reLine = regexp.MustCompile(":[0-9]*\n?$")
560
561 func cleanPath(s string) string {
562         s = rePath.ReplaceAllString(s, "/path.")
563         s = reLine.ReplaceAllString(s, ":xxx")
564         s = strings.Replace(s, "\n   ", "", -1)
565         s = strings.Replace(s, " /", "/", -1)
566         return s
567 }
568
569 func errToParts(err error) (a []string) {
570         for err != nil {
571                 var p testPrinter
572                 if xerrors.Unwrap(err) != nil {
573                         p.str += "wraps:"
574                 }
575                 f, ok := err.(xerrors.Formatter)
576                 if !ok {
577                         a = append(a, err.Error())
578                         break
579                 }
580                 err = f.FormatError(&p)
581                 a = append(a, cleanPath(p.str))
582         }
583         return a
584
585 }
586
587 type testPrinter struct {
588         str string
589 }
590
591 func (p *testPrinter) Print(a ...interface{}) {
592         p.str += fmt.Sprint(a...)
593 }
594
595 func (p *testPrinter) Printf(format string, a ...interface{}) {
596         p.str += fmt.Sprintf(format, a...)
597 }
598
599 func (p *testPrinter) Detail() bool {
600         p.str += " /"
601         return true
602 }