@@ -224,7 +224,7 @@ func TestFormatWithStack(t *testing.T) {
224224 }}
225225
226226 for i , tt := range tests {
227- testFormatCompleteCompare (t , i , tt .error , tt .format , tt .want )
227+ testFormatCompleteCompare (t , i , tt .error , tt .format , tt .want , true )
228228 }
229229}
230230
@@ -304,7 +304,60 @@ func TestFormatWithMessage(t *testing.T) {
304304 }}
305305
306306 for i , tt := range tests {
307- testFormatCompleteCompare (t , i , tt .error , tt .format , tt .want )
307+ testFormatCompleteCompare (t , i , tt .error , tt .format , tt .want , true )
308+ }
309+ }
310+
311+ func TestFormatGeneric (t * testing.T ) {
312+ starts := []struct {
313+ err error
314+ want []string
315+ }{
316+ {New ("new-error" ), []string {
317+ "new-error" ,
318+ "github.com/pkg/errors.TestFormatGeneric\n " +
319+ "\t .+/github.com/pkg/errors/format_test.go:311" },
320+ }, {Errorf ("errorf-error" ), []string {
321+ "errorf-error" ,
322+ "github.com/pkg/errors.TestFormatGeneric\n " +
323+ "\t .+/github.com/pkg/errors/format_test.go:315" },
324+ }, {errors .New ("errors-new-error" ), []string {
325+ "errors-new-error" },
326+ },
327+ }
328+
329+ wrappers := []wrapper {
330+ {
331+ func (err error ) error { return WithMessage (err , "with-message" ) },
332+ []string {"with-message" },
333+ }, {
334+ func (err error ) error { return WithStack (err ) },
335+ []string {
336+ "github.com/pkg/errors.TestFormatGeneric.func2\n \t " +
337+ ".+/github.com/pkg/errors/format_test.go:329" ,
338+ },
339+ }, {
340+ func (err error ) error { return Wrap (err , "wrap-error" ) },
341+ []string {
342+ "wrap-error" ,
343+ "github.com/pkg/errors.TestFormatGeneric.func3\n \t " +
344+ ".+/github.com/pkg/errors/format_test.go:335" ,
345+ },
346+ }, {
347+ func (err error ) error { return Wrapf (err , "wrapf-error%d" , 1 ) },
348+ []string {
349+ "wrapf-error1" ,
350+ "github.com/pkg/errors.TestFormatGeneric.func4\n \t " +
351+ ".+/github.com/pkg/errors/format_test.go:342" ,
352+ },
353+ },
354+ }
355+
356+ for s := range starts {
357+ err := starts [s ].err
358+ want := starts [s ].want
359+ testFormatCompleteCompare (t , s , err , "%+v" , want , false )
360+ testGenericRecursive (t , err , want , wrappers , 3 )
308361 }
309362}
310363
@@ -335,6 +388,9 @@ var stackLineR = regexp.MustCompile(`\.`)
335388// - incase entry contains a newline, its a stacktrace
336389// - incase entry contains no newline, its a solo line.
337390//
391+ // Detecting stack boundaries only works incase the WithStack-calls are
392+ // to be found on the same line, thats why it is optionally here.
393+ //
338394// Example use:
339395//
340396// for _, e := range blocks {
@@ -344,7 +400,8 @@ var stackLineR = regexp.MustCompile(`\.`)
344400// // Match as line
345401// }
346402// }
347- func parseBlocks (input string ) ([]string , error ) {
403+ //
404+ func parseBlocks (input string , detectStackboundaries bool ) ([]string , error ) {
348405 var blocks []string
349406
350407 stack := ""
@@ -363,15 +420,17 @@ func parseBlocks(input string) ([]string, error) {
363420 if wasStack {
364421 // Detecting two stacks after another, possible cause lines match in
365422 // our tests due to WithStack(WithStack(io.EOF)) on same line.
366- if lines [l ] {
367- if len (stack ) == 0 {
368- return nil , errors .New ("len of block must not be zero here" )
369- }
423+ if detectStackboundaries {
424+ if lines [l ] {
425+ if len (stack ) == 0 {
426+ return nil , errors .New ("len of block must not be zero here" )
427+ }
370428
371- blocks = append (blocks , stack )
372- stack = l
373- lines = map [string ]bool {l : true }
374- continue
429+ blocks = append (blocks , stack )
430+ stack = l
431+ lines = map [string ]bool {l : true }
432+ continue
433+ }
375434 }
376435
377436 stack = stack + "\n " + l
@@ -395,17 +454,17 @@ func parseBlocks(input string) ([]string, error) {
395454 return blocks , nil
396455}
397456
398- func testFormatCompleteCompare (t * testing.T , n int , arg interface {}, format string , want []string ) {
457+ func testFormatCompleteCompare (t * testing.T , n int , arg interface {}, format string , want []string , detectStackBoundaries bool ) {
399458 gotStr := fmt .Sprintf (format , arg )
400459
401- got , err := parseBlocks (gotStr )
460+ got , err := parseBlocks (gotStr , detectStackBoundaries )
402461 if err != nil {
403462 t .Fatal (err )
404463 }
405464
406465 if len (got ) != len (want ) {
407- t .Fatalf ("test %d: fmt.Sprintf(%s, err) -> wrong number of blocks: got(%d) want(%d)\n got: %q \n want: %q \n gotStr: %q" ,
408- n + 1 , format , len (got ), len (want ), got , want , gotStr )
466+ t .Fatalf ("test %d: fmt.Sprintf(%s, err) -> wrong number of blocks: got(%d) want(%d)\n got: %s \n want: %s \n gotStr: %q" ,
467+ n + 1 , format , len (got ), len (want ), prettyBlocks ( got ), prettyBlocks ( want ) , gotStr )
409468 }
410469
411470 for i := range got {
@@ -416,13 +475,62 @@ func testFormatCompleteCompare(t *testing.T, n int, arg interface{}, format stri
416475 t .Fatal (err )
417476 }
418477 if ! match {
419- t .Errorf ("test %d: block %d: fmt.Sprintf(%q, err):\n got: %q\n want: %q" , n + 1 , i + 1 , format , got [i ], want [i ])
478+ t .Fatalf ("test %d: block %d: fmt.Sprintf(%q, err):\n got:\n %q\n want:\n %q\n all-got:\n %s\n all-want:\n %s\n " ,
479+ n + 1 , i + 1 , format , got [i ], want [i ], prettyBlocks (got ), prettyBlocks (want ))
420480 }
421481 } else {
422482 // Match as message
423483 if got [i ] != want [i ] {
424- t .Errorf ("test %d: fmt.Sprintf(%s, err) at block %d got != want:\n got: %q\n want: %q" , n + 1 , format , i + 1 , got [i ], want [i ])
484+ t .Fatalf ("test %d: fmt.Sprintf(%s, err) at block %d got != want:\n got: %q\n want: %q" , n + 1 , format , i + 1 , got [i ], want [i ])
425485 }
426486 }
427487 }
428488}
489+
490+ type wrapper struct {
491+ wrap func (err error ) error
492+ want []string
493+ }
494+
495+ func prettyBlocks (blocks []string , prefix ... string ) string {
496+ var out []string
497+
498+ for _ , b := range blocks {
499+ out = append (out , fmt .Sprintf ("%v" , b ))
500+ }
501+
502+ return " " + strings .Join (out , "\n " )
503+ }
504+
505+ func testGenericRecursive (t * testing.T , beforeErr error , beforeWant []string , list []wrapper , maxDepth int ) {
506+ if len (beforeWant ) == 0 {
507+ panic ("beforeWant must not be empty" )
508+ }
509+ for _ , w := range list {
510+ if len (w .want ) == 0 {
511+ panic ("want must not be empty" )
512+ }
513+
514+ err := w .wrap (beforeErr )
515+
516+ // Copy required cause append(beforeWant, ..) modified beforeWant subtly.
517+ beforeCopy := make ([]string , len (beforeWant ))
518+ copy (beforeCopy , beforeWant )
519+
520+ beforeWant := beforeCopy
521+ last := len (beforeWant ) - 1
522+ var want []string
523+
524+ // Merge two stacks behind each other.
525+ if strings .ContainsAny (beforeWant [last ], "\n " ) && strings .ContainsAny (w .want [0 ], "\n " ) {
526+ want = append (beforeWant [:last ], append ([]string {beforeWant [last ] + "((?s).*)" + w .want [0 ]}, w .want [1 :]... )... )
527+ } else {
528+ want = append (beforeWant , w .want ... )
529+ }
530+
531+ testFormatCompleteCompare (t , maxDepth , err , "%+v" , want , false )
532+ if maxDepth > 0 {
533+ testGenericRecursive (t , err , want , list , maxDepth - 1 )
534+ }
535+ }
536+ }
0 commit comments