diff --git a/runnable_test.go b/runnable_test.go index 222646d..045be86 100644 --- a/runnable_test.go +++ b/runnable_test.go @@ -104,5 +104,4 @@ func TestRunnable(t *testing.T) { require.Error(t, err, context.DeadlineExceeded) assert.Equal(t, true, r.IsRunning()) }) - } diff --git a/with_panics_as_errors.go b/with_panics_as_errors.go new file mode 100644 index 0000000..2c97716 --- /dev/null +++ b/with_panics_as_errors.go @@ -0,0 +1,34 @@ +package runnable + +import ( + "context" + "fmt" +) + +type withPanicsAsErrors struct { +} + +func WithPanicsAsErrors() Option { + return &withPanicsAsErrors{} +} + +func (w *withPanicsAsErrors) apply(r *runnable) { + runFunc := r.runFunc + r.runFunc = func(ctx context.Context) error { + var err error + innerRun := func(ctx context.Context) error { + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("panic: %v", r) + } + }() + return runFunc(ctx) + } + + errDirect := innerRun(ctx) + if errDirect != nil { + return errDirect + } + return err + } +} diff --git a/with_panics_as_errors_test.go b/with_panics_as_errors_test.go new file mode 100644 index 0000000..2058249 --- /dev/null +++ b/with_panics_as_errors_test.go @@ -0,0 +1,72 @@ +package runnable + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestWithPanicsAsErrors(t *testing.T) { + + t.Run("panics as errors", func(t *testing.T) { + started, stopped := make(chan struct{}), make(chan struct{}) + + r := New(func(ctx context.Context) error { + started <- struct{}{} + panic("something went wrong") + return nil + }, WithPanicsAsErrors()) + + go func() { + err := r.Run(context.Background()) + require.Error(t, err) + stopped <- struct{}{} + }() + + <-started + <-stopped + }) + + t.Run("panics as errors, no panic", func(t *testing.T) { + started, stopped := make(chan struct{}), make(chan struct{}) + + r := New(func(ctx context.Context) error { + started <- struct{}{} + return nil + }, WithPanicsAsErrors()) + + go func() { + err := r.Run(context.Background()) + require.NoError(t, err) + stopped <- struct{}{} + }() + + <-started + <-stopped + }) + + t.Run("panics as errors, with stats", func(t *testing.T) { + started, stopped := make(chan struct{}), make(chan struct{}) + + store := NewStatusStore() + r := New(func(ctx context.Context) error { + started <- struct{}{} + panic("something went wrong") + return nil + }, WithPanicsAsErrors(), WithStatus("test", store)) + + go func() { + err := r.Run(context.Background()) + require.Error(t, err) + stopped <- struct{}{} + }() + + <-started + <-stopped + + s := store.Get() + require.Equal(t, false, s["test"].Running) + require.Error(t, s["test"].LastError) + }) +}