diff options
Diffstat (limited to '')
-rw-r--r-- | dependencies/pkg/mod/golang.org/x/xerrors@v0.0.0-20220907171357-04be3eba64a2/fmt_test.go | 602 |
1 files changed, 602 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/golang.org/x/xerrors@v0.0.0-20220907171357-04be3eba64a2/fmt_test.go b/dependencies/pkg/mod/golang.org/x/xerrors@v0.0.0-20220907171357-04be3eba64a2/fmt_test.go new file mode 100644 index 0000000..99d945f --- /dev/null +++ b/dependencies/pkg/mod/golang.org/x/xerrors@v0.0.0-20220907171357-04be3eba64a2/fmt_test.go @@ -0,0 +1,602 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xerrors_test + +import ( + "fmt" + "io" + "os" + "path" + "reflect" + "regexp" + "strconv" + "strings" + "testing" + + "golang.org/x/xerrors" +) + +func TestErrorf(t *testing.T) { + chained := &wrapped{"chained", nil} + chain := func(s ...string) (a []string) { + for _, s := range s { + a = append(a, cleanPath(s)) + } + return a + } + testCases := []struct { + got error + want []string + }{{ + xerrors.Errorf("no args"), + chain("no args/path.TestErrorf/path.go:xxx"), + }, { + xerrors.Errorf("no args: %s"), + chain("no args: %!s(MISSING)/path.TestErrorf/path.go:xxx"), + }, { + xerrors.Errorf("nounwrap: %s", "simple"), + chain(`nounwrap: simple/path.TestErrorf/path.go:xxx`), + }, { + xerrors.Errorf("nounwrap: %v", "simple"), + chain(`nounwrap: simple/path.TestErrorf/path.go:xxx`), + }, { + xerrors.Errorf("%s failed: %v", "foo", chained), + chain("foo failed/path.TestErrorf/path.go:xxx", + "chained/somefile.go:xxx"), + }, { + xerrors.Errorf("no wrap: %s", chained), + chain("no wrap/path.TestErrorf/path.go:xxx", + "chained/somefile.go:xxx"), + }, { + xerrors.Errorf("%s failed: %w", "foo", chained), + chain("wraps:foo failed/path.TestErrorf/path.go:xxx", + "chained/somefile.go:xxx"), + }, { + xerrors.Errorf("nowrapv: %v", chained), + chain("nowrapv/path.TestErrorf/path.go:xxx", + "chained/somefile.go:xxx"), + }, { + xerrors.Errorf("wrapw: %w", chained), + chain("wraps:wrapw/path.TestErrorf/path.go:xxx", + "chained/somefile.go:xxx"), + }, { + xerrors.Errorf("wrapw %w middle", chained), + chain("wraps:wrapw chained middle/path.TestErrorf/path.go:xxx", + "chained/somefile.go:xxx"), + }, { + xerrors.Errorf("not wrapped: %+v", chained), + chain("not wrapped: chained: somefile.go:123/path.TestErrorf/path.go:xxx"), + }} + for i, tc := range testCases { + t.Run(strconv.Itoa(i)+"/"+path.Join(tc.want...), func(t *testing.T) { + got := errToParts(tc.got) + if !reflect.DeepEqual(got, tc.want) { + t.Errorf("Format:\n got: %#v\nwant: %#v", got, tc.want) + } + + gotStr := tc.got.Error() + wantStr := fmt.Sprint(tc.got) + if gotStr != wantStr { + t.Errorf("Error:\n got: %#v\nwant: %#v", got, tc.want) + } + }) + } +} + +func TestErrorFormatter(t *testing.T) { + var ( + simple = &wrapped{"simple", nil} + elephant = &wrapped{ + "can't adumbrate elephant", + detailed{}, + } + nonascii = &wrapped{"café", nil} + newline = &wrapped{"msg with\nnewline", + &wrapped{"and another\none", nil}} + fallback = &wrapped{"fallback", os.ErrNotExist} + oldAndNew = &wrapped{"new style", formatError("old style")} + framed = &withFrameAndMore{ + frame: xerrors.Caller(0), + } + opaque = &wrapped{"outer", + xerrors.Opaque(&wrapped{"mid", + &wrapped{"inner", nil}})} + ) + testCases := []struct { + err error + fmt string + want string + regexp bool + }{{ + err: simple, + fmt: "%s", + want: "simple", + }, { + err: elephant, + fmt: "%s", + want: "can't adumbrate elephant: out of peanuts", + }, { + err: &wrapped{"a", &wrapped{"b", &wrapped{"c", nil}}}, + fmt: "%s", + want: "a: b: c", + }, { + err: simple, + fmt: "%+v", + want: "simple:" + + "\n somefile.go:123", + }, { + err: elephant, + fmt: "%+v", + want: "can't adumbrate elephant:" + + "\n somefile.go:123" + + "\n - out of peanuts:" + + "\n the elephant is on strike" + + "\n and the 12 monkeys" + + "\n are laughing", + }, { + err: &oneNewline{nil}, + fmt: "%+v", + want: "123", + }, { + err: &oneNewline{&oneNewline{nil}}, + fmt: "%+v", + want: "123:" + + "\n - 123", + }, { + err: &newlineAtEnd{nil}, + fmt: "%+v", + want: "newlineAtEnd:\n detail", + }, { + err: &newlineAtEnd{&newlineAtEnd{nil}}, + fmt: "%+v", + want: "newlineAtEnd:" + + "\n detail" + + "\n - newlineAtEnd:" + + "\n detail", + }, { + err: framed, + fmt: "%+v", + want: "something:" + + "\n golang.org/x/xerrors_test.TestErrorFormatter" + + "\n .+/fmt_test.go:101" + + "\n something more", + regexp: true, + }, { + err: fmtTwice("Hello World!"), + fmt: "%#v", + want: "2 times Hello World!", + }, { + err: fallback, + fmt: "%s", + want: "fallback: file does not exist", + }, { + err: fallback, + fmt: "%+v", + // Note: no colon after the last error, as there are no details. + want: "fallback:" + + "\n somefile.go:123" + + "\n - file does not exist", + }, { + err: opaque, + fmt: "%s", + want: "outer: mid: inner", + }, { + err: opaque, + fmt: "%+v", + want: "outer:" + + "\n somefile.go:123" + + "\n - mid:" + + "\n somefile.go:123" + + "\n - inner:" + + "\n somefile.go:123", + }, { + err: oldAndNew, + fmt: "%v", + want: "new style: old style", + }, { + err: oldAndNew, + fmt: "%q", + want: `"new style: old style"`, + }, { + err: oldAndNew, + fmt: "%+v", + // Note the extra indentation. + // Colon for old style error is rendered by the fmt.Formatter + // implementation of the old-style error. + want: "new style:" + + "\n somefile.go:123" + + "\n - old style:" + + "\n otherfile.go:456", + }, { + err: simple, + fmt: "%-12s", + want: "simple ", + }, { + // Don't use formatting flags for detailed view. + err: simple, + fmt: "%+12v", + want: "simple:" + + "\n somefile.go:123", + }, { + err: elephant, + fmt: "%+50s", + want: " can't adumbrate elephant: out of peanuts", + }, { + err: nonascii, + fmt: "%q", + want: `"café"`, + }, { + err: nonascii, + fmt: "%+q", + want: `"caf\u00e9"`, + }, { + err: simple, + fmt: "% x", + want: "73 69 6d 70 6c 65", + }, { + err: newline, + fmt: "%s", + want: "msg with" + + "\nnewline: and another" + + "\none", + }, { + err: newline, + fmt: "%+v", + want: "msg with" + + "\n newline:" + + "\n somefile.go:123" + + "\n - and another" + + "\n one:" + + "\n somefile.go:123", + }, { + err: &wrapped{"", &wrapped{"inner message", nil}}, + fmt: "%+v", + want: "somefile.go:123" + + "\n - inner message:" + + "\n somefile.go:123", + }, { + err: spurious(""), + fmt: "%s", + want: "spurious", + }, { + err: spurious(""), + fmt: "%+v", + want: "spurious", + }, { + err: spurious("extra"), + fmt: "%s", + want: "spurious", + }, { + err: spurious("extra"), + fmt: "%+v", + want: "spurious:\n" + + " extra", + }, { + err: nil, + fmt: "%+v", + want: "<nil>", + }, { + err: (*wrapped)(nil), + fmt: "%+v", + want: "<nil>", + }, { + err: simple, + fmt: "%T", + want: "*xerrors_test.wrapped", + }, { + err: simple, + fmt: "%🤪", + want: "%!🤪(*xerrors_test.wrapped)", + // For 1.13: + // want: "%!🤪(*xerrors_test.wrapped=&{simple <nil>})", + }, { + err: formatError("use fmt.Formatter"), + fmt: "%#v", + want: "use fmt.Formatter", + }, { + err: fmtTwice("%s %s", "ok", panicValue{}), + fmt: "%s", + // Different Go versions produce different results. + want: `ok %!s\(PANIC=(String method: )?panic\)/ok %!s\(PANIC=(String method: )?panic\)`, + regexp: true, + }, { + err: fmtTwice("%o %s", panicValue{}, "ok"), + fmt: "%s", + want: "{} ok/{} ok", + }, { + err: adapted{"adapted", nil}, + fmt: "%+v", + want: "adapted:" + + "\n detail", + }, { + err: adapted{"outer", adapted{"mid", adapted{"inner", nil}}}, + fmt: "%+v", + want: "outer:" + + "\n detail" + + "\n - mid:" + + "\n detail" + + "\n - inner:" + + "\n detail", + }} + for i, tc := range testCases { + t.Run(fmt.Sprintf("%d/%s", i, tc.fmt), func(t *testing.T) { + got := fmt.Sprintf(tc.fmt, tc.err) + var ok bool + if tc.regexp { + var err error + ok, err = regexp.MatchString(tc.want+"$", got) + if err != nil { + t.Fatal(err) + } + } else { + ok = got == tc.want + } + if !ok { + t.Errorf("\n got: %q\nwant: %q", got, tc.want) + } + }) + } +} + +func TestAdaptor(t *testing.T) { + testCases := []struct { + err error + fmt string + want string + regexp bool + }{{ + err: adapted{"adapted", nil}, + fmt: "%+v", + want: "adapted:" + + "\n detail", + }, { + err: adapted{"outer", adapted{"mid", adapted{"inner", nil}}}, + fmt: "%+v", + want: "outer:" + + "\n detail" + + "\n - mid:" + + "\n detail" + + "\n - inner:" + + "\n detail", + }} + for i, tc := range testCases { + t.Run(fmt.Sprintf("%d/%s", i, tc.fmt), func(t *testing.T) { + got := fmt.Sprintf(tc.fmt, tc.err) + if got != tc.want { + t.Errorf("\n got: %q\nwant: %q", got, tc.want) + } + }) + } +} + +var _ xerrors.Formatter = wrapped{} + +type wrapped struct { + msg string + err error +} + +func (e wrapped) Error() string { return "should call Format" } + +func (e wrapped) Format(s fmt.State, verb rune) { + xerrors.FormatError(&e, s, verb) +} + +func (e wrapped) FormatError(p xerrors.Printer) (next error) { + p.Print(e.msg) + p.Detail() + p.Print("somefile.go:123") + return e.err +} + +var _ xerrors.Formatter = detailed{} + +type detailed struct{} + +func (e detailed) Error() string { panic("should have called FormatError") } + +func (detailed) FormatError(p xerrors.Printer) (next error) { + p.Printf("out of %s", "peanuts") + p.Detail() + p.Print("the elephant is on strike\n") + p.Printf("and the %d monkeys\nare laughing", 12) + return nil +} + +type withFrameAndMore struct { + frame xerrors.Frame +} + +func (e *withFrameAndMore) Error() string { return fmt.Sprint(e) } + +func (e *withFrameAndMore) Format(s fmt.State, v rune) { + xerrors.FormatError(e, s, v) +} + +func (e *withFrameAndMore) FormatError(p xerrors.Printer) (next error) { + p.Print("something") + if p.Detail() { + e.frame.Format(p) + p.Print("something more") + } + return nil +} + +type spurious string + +func (e spurious) Error() string { return fmt.Sprint(e) } + +// move to 1_12 test file +func (e spurious) Format(s fmt.State, verb rune) { + xerrors.FormatError(e, s, verb) +} + +func (e spurious) FormatError(p xerrors.Printer) (next error) { + p.Print("spurious") + p.Detail() // Call detail even if we don't print anything + if e == "" { + p.Print() + } else { + p.Print("\n", string(e)) // print extraneous leading newline + } + return nil +} + +type oneNewline struct { + next error +} + +func (e *oneNewline) Error() string { return fmt.Sprint(e) } + +func (e *oneNewline) Format(s fmt.State, verb rune) { + xerrors.FormatError(e, s, verb) +} + +func (e *oneNewline) FormatError(p xerrors.Printer) (next error) { + p.Print("1") + p.Print("2") + p.Print("3") + p.Detail() + p.Print("\n") + return e.next +} + +type newlineAtEnd struct { + next error +} + +func (e *newlineAtEnd) Error() string { return fmt.Sprint(e) } + +func (e *newlineAtEnd) Format(s fmt.State, verb rune) { + xerrors.FormatError(e, s, verb) +} + +func (e *newlineAtEnd) FormatError(p xerrors.Printer) (next error) { + p.Print("newlineAtEnd") + p.Detail() + p.Print("detail\n") + return e.next +} + +type adapted struct { + msg string + err error +} + +func (e adapted) Error() string { return e.msg } + +func (e adapted) Format(s fmt.State, verb rune) { + xerrors.FormatError(e, s, verb) +} + +func (e adapted) FormatError(p xerrors.Printer) error { + p.Print(e.msg) + p.Detail() + p.Print("detail") + return e.err +} + +// formatError is an error implementing Format instead of xerrors.Formatter. +// The implementation mimics the implementation of github.com/pkg/errors. +type formatError string + +func (e formatError) Error() string { return string(e) } + +func (e formatError) Format(s fmt.State, verb rune) { + // Body based on pkg/errors/errors.go + switch verb { + case 'v': + if s.Flag('+') { + io.WriteString(s, string(e)) + fmt.Fprintf(s, ":\n%s", "otherfile.go:456") + return + } + fallthrough + case 's': + io.WriteString(s, string(e)) + case 'q': + fmt.Fprintf(s, "%q", string(e)) + } +} + +func (e formatError) GoString() string { + panic("should never be called") +} + +type fmtTwiceErr struct { + format string + args []interface{} +} + +func fmtTwice(format string, a ...interface{}) error { + return fmtTwiceErr{format, a} +} + +func (e fmtTwiceErr) Error() string { return fmt.Sprint(e) } + +func (e fmtTwiceErr) Format(s fmt.State, verb rune) { + xerrors.FormatError(e, s, verb) +} + +func (e fmtTwiceErr) FormatError(p xerrors.Printer) (next error) { + p.Printf(e.format, e.args...) + p.Print("/") + p.Printf(e.format, e.args...) + return nil +} + +func (e fmtTwiceErr) GoString() string { + return "2 times " + fmt.Sprintf(e.format, e.args...) +} + +type panicValue struct{} + +func (panicValue) String() string { panic("panic") } + +var rePath = regexp.MustCompile(`( [^ ]*)xerrors.*test\.`) +var reLine = regexp.MustCompile(":[0-9]*\n?$") + +func cleanPath(s string) string { + s = rePath.ReplaceAllString(s, "/path.") + s = reLine.ReplaceAllString(s, ":xxx") + s = strings.Replace(s, "\n ", "", -1) + s = strings.Replace(s, " /", "/", -1) + return s +} + +func errToParts(err error) (a []string) { + for err != nil { + var p testPrinter + if xerrors.Unwrap(err) != nil { + p.str += "wraps:" + } + f, ok := err.(xerrors.Formatter) + if !ok { + a = append(a, err.Error()) + break + } + err = f.FormatError(&p) + a = append(a, cleanPath(p.str)) + } + return a + +} + +type testPrinter struct { + str string +} + +func (p *testPrinter) Print(a ...interface{}) { + p.str += fmt.Sprint(a...) +} + +func (p *testPrinter) Printf(format string, a ...interface{}) { + p.str += fmt.Sprintf(format, a...) +} + +func (p *testPrinter) Detail() bool { + p.str += " /" + return true +} |