Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/20251015114355.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:gear: `[parallelisation]` Report the context cancellation cause in the related error to provide more context
6 changes: 5 additions & 1 deletion utils/parallelisation/contextual.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import (

// DetermineContextError determines what the context error is if any.
func DetermineContextError(ctx context.Context) error {
return commonerrors.ConvertContextError(ctx.Err())
err := commonerrors.ConvertContextError(ctx.Err())
if commonerrors.Any(err, nil) {
return err
}
return commonerrors.WrapError(err, context.Cause(ctx), "")
}

type ContextualFunc func(ctx context.Context) error
Expand Down
37 changes: 37 additions & 0 deletions utils/parallelisation/contextual_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package parallelisation

import (
"context"
"errors"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/ARM-software/golang-utils/utils/commonerrors"
Expand Down Expand Up @@ -49,3 +52,37 @@ func TestForEach(t *testing.T) {
require.NoError(t, ForEach(context.Background(), WithOptions(Workers(5), JoinErrors), WrapCancelToContextualFunc(cancelFunc), WrapCancelToContextualFunc(cancelFunc), WrapCancelToContextualFunc(cancelFunc)))
})
}

func TestDetermineContextError(t *testing.T) {
t.Run("normal", func(t *testing.T) {
require.NoError(t, DetermineContextError(context.Background()))
})
t.Run("cancellation", func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
require.NoError(t, DetermineContextError(ctx))
cancel()
err := DetermineContextError(ctx)
errortest.AssertError(t, err, commonerrors.ErrTimeout, commonerrors.ErrCancelled)
})
t.Run("cancellation with cause", func(t *testing.T) {
cause := errors.New("a cause")
ctx, cancel := context.WithCancelCause(context.Background())
defer cancel(cause)
require.NoError(t, DetermineContextError(ctx))
cancel(cause)
err := DetermineContextError(ctx)
errortest.AssertError(t, err, commonerrors.ErrTimeout, commonerrors.ErrCancelled)
errortest.AssertErrorDescription(t, err, cause.Error())
})
t.Run("cancellation with timeout cause", func(t *testing.T) {
cause := errors.New("a cause")
ctx, cancel := context.WithTimeoutCause(context.Background(), 5*time.Second, cause)
defer cancel()
require.NoError(t, DetermineContextError(ctx))
cancel()
err := DetermineContextError(ctx)
errortest.RequireError(t, err, commonerrors.ErrTimeout, commonerrors.ErrCancelled)
assert.NotContains(t, err.Error(), cause.Error()) // the timeout did not take effect and a cancellation was performed instead so the cause is not passed through
})
}
Loading