Skip to content

Commit

Permalink
Merge pull request #96 from alitto/fix/AD/stack-traces
Browse files Browse the repository at this point in the history
fix(panics): wrap panic errors correctly and include stack trace
  • Loading branch information
alitto authored Dec 25, 2024
2 parents ac7c6a3 + d1ff3d3 commit dfb31e5
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 5 deletions.
3 changes: 2 additions & 1 deletion default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pond

import (
"errors"
"strings"
"sync/atomic"
"testing"

Expand Down Expand Up @@ -41,7 +42,7 @@ func TestSubmitWithPanic(t *testing.T) {
err := task.Wait()

assert.True(t, errors.Is(err, ErrPanic))
assert.Equal(t, "task panicked: dummy panic", err.Error())
assert.True(t, strings.HasPrefix(err.Error(), "task panicked: dummy panic"))
}

func TestNewGroup(t *testing.T) {
Expand Down
20 changes: 18 additions & 2 deletions pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package pond
import (
"context"
"errors"
"regexp"
"sync/atomic"
"testing"
"time"
Expand Down Expand Up @@ -48,14 +49,29 @@ func TestPoolSubmitWithPanic(t *testing.T) {

pool := NewPool(100)

sampleErr := errors.New("sample error")

task := pool.Submit(func() {
panic("dummy panic")
panic(sampleErr)
})

err := task.Wait()

// The returned error should be a wrapped error containing the panic error.
assert.True(t, errors.Is(err, ErrPanic))
assert.Equal(t, "task panicked: dummy panic", err.Error())
assert.True(t, errors.Is(err, sampleErr))

wrappedErrors := (err).(interface {
Unwrap() []error
}).Unwrap()

assert.Equal(t, 2, len(wrappedErrors))
assert.Equal(t, ErrPanic, wrappedErrors[0])
assert.Equal(t, sampleErr, wrappedErrors[1])

matches, err := regexp.MatchString(`task panicked: sample error, goroutine \d+ \[running\]:\n\s*runtime/debug\.Stack\(\)`, err.Error())
assert.True(t, matches)
assert.Equal(t, nil, err)
}

func TestPoolSubmitWithErr(t *testing.T) {
Expand Down
3 changes: 2 additions & 1 deletion result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pond

import (
"errors"
"strings"
"sync/atomic"
"testing"

Expand Down Expand Up @@ -34,7 +35,7 @@ func TestResultPoolSubmitTaskWithPanic(t *testing.T) {
output, err := task.Wait()

assert.True(t, errors.Is(err, ErrPanic))
assert.Equal(t, "task panicked: dummy panic", err.Error())
assert.True(t, strings.HasPrefix(err.Error(), "task panicked: dummy panic"))
assert.Equal(t, 0, output)
}

Expand Down
7 changes: 6 additions & 1 deletion task.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package pond
import (
"errors"
"fmt"
"runtime/debug"
"sync"
)

Expand Down Expand Up @@ -62,7 +63,11 @@ func wrapTask[R any, C func(error) | func(R, error)](task any, callback C) func(
func invokeTask[R any](task any) (output R, err error) {
defer func() {
if p := recover(); p != nil {
err = fmt.Errorf("%w: %v", ErrPanic, p)
if e, ok := p.(error); ok {
err = fmt.Errorf("%w: %w, %s", ErrPanic, e, debug.Stack())
} else {
err = fmt.Errorf("%w: %v, %s", ErrPanic, p, debug.Stack())
}
return
}
}()
Expand Down

0 comments on commit dfb31e5

Please sign in to comment.