123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535 |
- package errors
- import (
- "errors"
- "fmt"
- "io"
- "regexp"
- "strings"
- "testing"
- )
- func TestFormatNew(t *testing.T) {
- tests := []struct {
- error
- format string
- want string
- }{{
- New("error"),
- "%s",
- "error",
- }, {
- New("error"),
- "%v",
- "error",
- }, {
- New("error"),
- "%+v",
- "error\n" +
- "github.com/pkg/errors.TestFormatNew\n" +
- "\t.+/github.com/pkg/errors/format_test.go:26",
- }, {
- New("error"),
- "%q",
- `"error"`,
- }}
- for i, tt := range tests {
- testFormatRegexp(t, i, tt.error, tt.format, tt.want)
- }
- }
- func TestFormatErrorf(t *testing.T) {
- tests := []struct {
- error
- format string
- want string
- }{{
- Errorf("%s", "error"),
- "%s",
- "error",
- }, {
- Errorf("%s", "error"),
- "%v",
- "error",
- }, {
- Errorf("%s", "error"),
- "%+v",
- "error\n" +
- "github.com/pkg/errors.TestFormatErrorf\n" +
- "\t.+/github.com/pkg/errors/format_test.go:56",
- }}
- for i, tt := range tests {
- testFormatRegexp(t, i, tt.error, tt.format, tt.want)
- }
- }
- func TestFormatWrap(t *testing.T) {
- tests := []struct {
- error
- format string
- want string
- }{{
- Wrap(New("error"), "error2"),
- "%s",
- "error2: error",
- }, {
- Wrap(New("error"), "error2"),
- "%v",
- "error2: error",
- }, {
- Wrap(New("error"), "error2"),
- "%+v",
- "error\n" +
- "github.com/pkg/errors.TestFormatWrap\n" +
- "\t.+/github.com/pkg/errors/format_test.go:82",
- }, {
- Wrap(io.EOF, "error"),
- "%s",
- "error: EOF",
- }, {
- Wrap(io.EOF, "error"),
- "%v",
- "error: EOF",
- }, {
- Wrap(io.EOF, "error"),
- "%+v",
- "EOF\n" +
- "error\n" +
- "github.com/pkg/errors.TestFormatWrap\n" +
- "\t.+/github.com/pkg/errors/format_test.go:96",
- }, {
- Wrap(Wrap(io.EOF, "error1"), "error2"),
- "%+v",
- "EOF\n" +
- "error1\n" +
- "github.com/pkg/errors.TestFormatWrap\n" +
- "\t.+/github.com/pkg/errors/format_test.go:103\n",
- }, {
- Wrap(New("error with space"), "context"),
- "%q",
- `"context: error with space"`,
- }}
- for i, tt := range tests {
- testFormatRegexp(t, i, tt.error, tt.format, tt.want)
- }
- }
- func TestFormatWrapf(t *testing.T) {
- tests := []struct {
- error
- format string
- want string
- }{{
- Wrapf(io.EOF, "error%d", 2),
- "%s",
- "error2: EOF",
- }, {
- Wrapf(io.EOF, "error%d", 2),
- "%v",
- "error2: EOF",
- }, {
- Wrapf(io.EOF, "error%d", 2),
- "%+v",
- "EOF\n" +
- "error2\n" +
- "github.com/pkg/errors.TestFormatWrapf\n" +
- "\t.+/github.com/pkg/errors/format_test.go:134",
- }, {
- Wrapf(New("error"), "error%d", 2),
- "%s",
- "error2: error",
- }, {
- Wrapf(New("error"), "error%d", 2),
- "%v",
- "error2: error",
- }, {
- Wrapf(New("error"), "error%d", 2),
- "%+v",
- "error\n" +
- "github.com/pkg/errors.TestFormatWrapf\n" +
- "\t.+/github.com/pkg/errors/format_test.go:149",
- }}
- for i, tt := range tests {
- testFormatRegexp(t, i, tt.error, tt.format, tt.want)
- }
- }
- func TestFormatWithStack(t *testing.T) {
- tests := []struct {
- error
- format string
- want []string
- }{{
- WithStack(io.EOF),
- "%s",
- []string{"EOF"},
- }, {
- WithStack(io.EOF),
- "%v",
- []string{"EOF"},
- }, {
- WithStack(io.EOF),
- "%+v",
- []string{"EOF",
- "github.com/pkg/errors.TestFormatWithStack\n" +
- "\t.+/github.com/pkg/errors/format_test.go:175"},
- }, {
- WithStack(New("error")),
- "%s",
- []string{"error"},
- }, {
- WithStack(New("error")),
- "%v",
- []string{"error"},
- }, {
- WithStack(New("error")),
- "%+v",
- []string{"error",
- "github.com/pkg/errors.TestFormatWithStack\n" +
- "\t.+/github.com/pkg/errors/format_test.go:189",
- "github.com/pkg/errors.TestFormatWithStack\n" +
- "\t.+/github.com/pkg/errors/format_test.go:189"},
- }, {
- WithStack(WithStack(io.EOF)),
- "%+v",
- []string{"EOF",
- "github.com/pkg/errors.TestFormatWithStack\n" +
- "\t.+/github.com/pkg/errors/format_test.go:197",
- "github.com/pkg/errors.TestFormatWithStack\n" +
- "\t.+/github.com/pkg/errors/format_test.go:197"},
- }, {
- WithStack(WithStack(Wrapf(io.EOF, "message"))),
- "%+v",
- []string{"EOF",
- "message",
- "github.com/pkg/errors.TestFormatWithStack\n" +
- "\t.+/github.com/pkg/errors/format_test.go:205",
- "github.com/pkg/errors.TestFormatWithStack\n" +
- "\t.+/github.com/pkg/errors/format_test.go:205",
- "github.com/pkg/errors.TestFormatWithStack\n" +
- "\t.+/github.com/pkg/errors/format_test.go:205"},
- }, {
- WithStack(Errorf("error%d", 1)),
- "%+v",
- []string{"error1",
- "github.com/pkg/errors.TestFormatWithStack\n" +
- "\t.+/github.com/pkg/errors/format_test.go:216",
- "github.com/pkg/errors.TestFormatWithStack\n" +
- "\t.+/github.com/pkg/errors/format_test.go:216"},
- }}
- for i, tt := range tests {
- testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true)
- }
- }
- func TestFormatWithMessage(t *testing.T) {
- tests := []struct {
- error
- format string
- want []string
- }{{
- WithMessage(New("error"), "error2"),
- "%s",
- []string{"error2: error"},
- }, {
- WithMessage(New("error"), "error2"),
- "%v",
- []string{"error2: error"},
- }, {
- WithMessage(New("error"), "error2"),
- "%+v",
- []string{
- "error",
- "github.com/pkg/errors.TestFormatWithMessage\n" +
- "\t.+/github.com/pkg/errors/format_test.go:244",
- "error2"},
- }, {
- WithMessage(io.EOF, "addition1"),
- "%s",
- []string{"addition1: EOF"},
- }, {
- WithMessage(io.EOF, "addition1"),
- "%v",
- []string{"addition1: EOF"},
- }, {
- WithMessage(io.EOF, "addition1"),
- "%+v",
- []string{"EOF", "addition1"},
- }, {
- WithMessage(WithMessage(io.EOF, "addition1"), "addition2"),
- "%v",
- []string{"addition2: addition1: EOF"},
- }, {
- WithMessage(WithMessage(io.EOF, "addition1"), "addition2"),
- "%+v",
- []string{"EOF", "addition1", "addition2"},
- }, {
- Wrap(WithMessage(io.EOF, "error1"), "error2"),
- "%+v",
- []string{"EOF", "error1", "error2",
- "github.com/pkg/errors.TestFormatWithMessage\n" +
- "\t.+/github.com/pkg/errors/format_test.go:272"},
- }, {
- WithMessage(Errorf("error%d", 1), "error2"),
- "%+v",
- []string{"error1",
- "github.com/pkg/errors.TestFormatWithMessage\n" +
- "\t.+/github.com/pkg/errors/format_test.go:278",
- "error2"},
- }, {
- WithMessage(WithStack(io.EOF), "error"),
- "%+v",
- []string{
- "EOF",
- "github.com/pkg/errors.TestFormatWithMessage\n" +
- "\t.+/github.com/pkg/errors/format_test.go:285",
- "error"},
- }, {
- WithMessage(Wrap(WithStack(io.EOF), "inside-error"), "outside-error"),
- "%+v",
- []string{
- "EOF",
- "github.com/pkg/errors.TestFormatWithMessage\n" +
- "\t.+/github.com/pkg/errors/format_test.go:293",
- "inside-error",
- "github.com/pkg/errors.TestFormatWithMessage\n" +
- "\t.+/github.com/pkg/errors/format_test.go:293",
- "outside-error"},
- }}
- for i, tt := range tests {
- testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want, true)
- }
- }
- func TestFormatGeneric(t *testing.T) {
- starts := []struct {
- err error
- want []string
- }{
- {New("new-error"), []string{
- "new-error",
- "github.com/pkg/errors.TestFormatGeneric\n" +
- "\t.+/github.com/pkg/errors/format_test.go:315"},
- }, {Errorf("errorf-error"), []string{
- "errorf-error",
- "github.com/pkg/errors.TestFormatGeneric\n" +
- "\t.+/github.com/pkg/errors/format_test.go:319"},
- }, {errors.New("errors-new-error"), []string{
- "errors-new-error"},
- },
- }
- wrappers := []wrapper{
- {
- func(err error) error { return WithMessage(err, "with-message") },
- []string{"with-message"},
- }, {
- func(err error) error { return WithStack(err) },
- []string{
- "github.com/pkg/errors.(func·002|TestFormatGeneric.func2)\n\t" +
- ".+/github.com/pkg/errors/format_test.go:333",
- },
- }, {
- func(err error) error { return Wrap(err, "wrap-error") },
- []string{
- "wrap-error",
- "github.com/pkg/errors.(func·003|TestFormatGeneric.func3)\n\t" +
- ".+/github.com/pkg/errors/format_test.go:339",
- },
- }, {
- func(err error) error { return Wrapf(err, "wrapf-error%d", 1) },
- []string{
- "wrapf-error1",
- "github.com/pkg/errors.(func·004|TestFormatGeneric.func4)\n\t" +
- ".+/github.com/pkg/errors/format_test.go:346",
- },
- },
- }
- for s := range starts {
- err := starts[s].err
- want := starts[s].want
- testFormatCompleteCompare(t, s, err, "%+v", want, false)
- testGenericRecursive(t, err, want, wrappers, 3)
- }
- }
- func testFormatRegexp(t *testing.T, n int, arg interface{}, format, want string) {
- got := fmt.Sprintf(format, arg)
- gotLines := strings.SplitN(got, "\n", -1)
- wantLines := strings.SplitN(want, "\n", -1)
- if len(wantLines) > len(gotLines) {
- t.Errorf("test %d: wantLines(%d) > gotLines(%d):\n got: %q\nwant: %q", n+1, len(wantLines), len(gotLines), got, want)
- return
- }
- for i, w := range wantLines {
- match, err := regexp.MatchString(w, gotLines[i])
- if err != nil {
- t.Fatal(err)
- }
- if !match {
- t.Errorf("test %d: line %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, got, want)
- }
- }
- }
- var stackLineR = regexp.MustCompile(`\.`)
- // parseBlocks parses input into a slice, where:
- // - incase entry contains a newline, its a stacktrace
- // - incase entry contains no newline, its a solo line.
- //
- // Detecting stack boundaries only works incase the WithStack-calls are
- // to be found on the same line, thats why it is optionally here.
- //
- // Example use:
- //
- // for _, e := range blocks {
- // if strings.ContainsAny(e, "\n") {
- // // Match as stack
- // } else {
- // // Match as line
- // }
- // }
- //
- func parseBlocks(input string, detectStackboundaries bool) ([]string, error) {
- var blocks []string
- stack := ""
- wasStack := false
- lines := map[string]bool{} // already found lines
- for _, l := range strings.Split(input, "\n") {
- isStackLine := stackLineR.MatchString(l)
- switch {
- case !isStackLine && wasStack:
- blocks = append(blocks, stack, l)
- stack = ""
- lines = map[string]bool{}
- case isStackLine:
- if wasStack {
- // Detecting two stacks after another, possible cause lines match in
- // our tests due to WithStack(WithStack(io.EOF)) on same line.
- if detectStackboundaries {
- if lines[l] {
- if len(stack) == 0 {
- return nil, errors.New("len of block must not be zero here")
- }
- blocks = append(blocks, stack)
- stack = l
- lines = map[string]bool{l: true}
- continue
- }
- }
- stack = stack + "\n" + l
- } else {
- stack = l
- }
- lines[l] = true
- case !isStackLine && !wasStack:
- blocks = append(blocks, l)
- default:
- return nil, errors.New("must not happen")
- }
- wasStack = isStackLine
- }
- // Use up stack
- if stack != "" {
- blocks = append(blocks, stack)
- }
- return blocks, nil
- }
- func testFormatCompleteCompare(t *testing.T, n int, arg interface{}, format string, want []string, detectStackBoundaries bool) {
- gotStr := fmt.Sprintf(format, arg)
- got, err := parseBlocks(gotStr, detectStackBoundaries)
- if err != nil {
- t.Fatal(err)
- }
- if len(got) != len(want) {
- t.Fatalf("test %d: fmt.Sprintf(%s, err) -> wrong number of blocks: got(%d) want(%d)\n got: %s\nwant: %s\ngotStr: %q",
- n+1, format, len(got), len(want), prettyBlocks(got), prettyBlocks(want), gotStr)
- }
- for i := range got {
- if strings.ContainsAny(want[i], "\n") {
- // Match as stack
- match, err := regexp.MatchString(want[i], got[i])
- if err != nil {
- t.Fatal(err)
- }
- if !match {
- 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",
- n+1, i+1, format, got[i], want[i], prettyBlocks(got), prettyBlocks(want))
- }
- } else {
- // Match as message
- if got[i] != want[i] {
- 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])
- }
- }
- }
- }
- type wrapper struct {
- wrap func(err error) error
- want []string
- }
- func prettyBlocks(blocks []string, prefix ...string) string {
- var out []string
- for _, b := range blocks {
- out = append(out, fmt.Sprintf("%v", b))
- }
- return " " + strings.Join(out, "\n ")
- }
- func testGenericRecursive(t *testing.T, beforeErr error, beforeWant []string, list []wrapper, maxDepth int) {
- if len(beforeWant) == 0 {
- panic("beforeWant must not be empty")
- }
- for _, w := range list {
- if len(w.want) == 0 {
- panic("want must not be empty")
- }
- err := w.wrap(beforeErr)
- // Copy required cause append(beforeWant, ..) modified beforeWant subtly.
- beforeCopy := make([]string, len(beforeWant))
- copy(beforeCopy, beforeWant)
- beforeWant := beforeCopy
- last := len(beforeWant) - 1
- var want []string
- // Merge two stacks behind each other.
- if strings.ContainsAny(beforeWant[last], "\n") && strings.ContainsAny(w.want[0], "\n") {
- want = append(beforeWant[:last], append([]string{beforeWant[last] + "((?s).*)" + w.want[0]}, w.want[1:]...)...)
- } else {
- want = append(beforeWant, w.want...)
- }
- testFormatCompleteCompare(t, maxDepth, err, "%+v", want, false)
- if maxDepth > 0 {
- testGenericRecursive(t, err, want, list, maxDepth-1)
- }
- }
- }
|