| 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:25", |
| }, { |
| New("error"), |
| "%q", |
| `"error"`, |
| "\t.+/github.com/pkg/errors/format_test.go:26", |
| }} |
| |
| 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:55", |
| }} |
| |
| 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:81", |
| }, { |
| 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:95", |
| }, { |
| 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:102\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:133", |
| }, { |
| 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:148", |
| }} |
| |
| 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:171"}, |
| }, { |
| 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:185", |
| "github.com/pkg/errors.TestFormatWithStack\n" + |
| "\t.+/github.com/pkg/errors/format_test.go:185"}, |
| }, { |
| WithStack(WithStack(io.EOF)), |
| "%+v", |
| []string{"EOF", |
| "github.com/pkg/errors.TestFormatWithStack\n" + |
| "\t.+/github.com/pkg/errors/format_test.go:193", |
| "github.com/pkg/errors.TestFormatWithStack\n" + |
| "\t.+/github.com/pkg/errors/format_test.go:193"}, |
| }, { |
| WithStack(WithStack(Wrapf(io.EOF, "message"))), |
| "%+v", |
| []string{"EOF", |
| "message", |
| "github.com/pkg/errors.TestFormatWithStack\n" + |
| "\t.+/github.com/pkg/errors/format_test.go:201", |
| "github.com/pkg/errors.TestFormatWithStack\n" + |
| "\t.+/github.com/pkg/errors/format_test.go:201", |
| "github.com/pkg/errors.TestFormatWithStack\n" + |
| "\t.+/github.com/pkg/errors/format_test.go:201"}, |
| }, { |
| WithStack(Errorf("error%d", 1)), |
| "%+v", |
| []string{"error1", |
| "github.com/pkg/errors.TestFormatWithStack\n" + |
| "\t.+/github.com/pkg/errors/format_test.go:212", |
| "github.com/pkg/errors.TestFormatWithStack\n" + |
| "\t.+/github.com/pkg/errors/format_test.go:212"}, |
| }} |
| |
| for i, tt := range tests { |
| testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want) |
| } |
| } |
| |
| 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:240", |
| "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:268"}, |
| }, { |
| WithMessage(Errorf("error%d", 1), "error2"), |
| "%+v", |
| []string{"error1", |
| "github.com/pkg/errors.TestFormatWithMessage\n" + |
| "\t.+/github.com/pkg/errors/format_test.go:274", |
| "error2"}, |
| }, { |
| WithMessage(WithStack(io.EOF), "error"), |
| "%+v", |
| []string{ |
| "EOF", |
| "github.com/pkg/errors.TestFormatWithMessage\n" + |
| "\t.+/github.com/pkg/errors/format_test.go:281", |
| "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:289", |
| "inside-error", |
| "github.com/pkg/errors.TestFormatWithMessage\n" + |
| "\t.+/github.com/pkg/errors/format_test.go:289", |
| "outside-error"}, |
| }} |
| |
| for i, tt := range tests { |
| testFormatCompleteCompare(t, i, tt.error, tt.format, tt.want) |
| } |
| } |
| |
| 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. |
| // |
| // Example use: |
| // |
| // for _, e := range blocks { |
| // if strings.ContainsAny(e, "\n") { |
| // // Match as stack |
| // } else { |
| // // Match as line |
| // } |
| // } |
| func parseBlocks(input string) ([]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 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) { |
| gotStr := fmt.Sprintf(format, arg) |
| |
| got, err := parseBlocks(gotStr) |
| 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: %q\nwant: %q\ngotStr: %q", |
| n+1, format, len(got), len(want), got, 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.Errorf("test %d: block %d: fmt.Sprintf(%q, err):\n got: %q\nwant: %q", n+1, i+1, format, got[i], want[i]) |
| } |
| } else { |
| // Match as message |
| if got[i] != want[i] { |
| t.Errorf("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]) |
| } |
| } |
| } |
| } |