|
|
@@ -1,6 +1,7 @@
|
|
|
package errors
|
|
|
|
|
|
import (
|
|
|
+ "errors"
|
|
|
"fmt"
|
|
|
"io"
|
|
|
"regexp"
|
|
|
@@ -31,6 +32,7 @@ func TestFormatNew(t *testing.T) {
|
|
|
New("error"),
|
|
|
"%q",
|
|
|
`"error"`,
|
|
|
+ "\t.+/github.com/pkg/errors/format_test.go:26",
|
|
|
}}
|
|
|
|
|
|
for i, tt := range tests {
|
|
|
@@ -157,16 +159,270 @@ func TestFormatWrapf(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+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)
|
|
|
- lines := strings.SplitN(got, "\n", -1)
|
|
|
- for i, w := range strings.SplitN(want, "\n", -1) {
|
|
|
- match, err := regexp.MatchString(w, lines[i])
|
|
|
+ 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): got: %q, want: %q", n+1, i+1, format, got, want)
|
|
|
+ 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])
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|