Skip to content

Commit

Permalink
use generics for asyncTasks, no type assertion required (#14)
Browse files Browse the repository at this point in the history
* tweaks

* add test for generic version

* continueFunc in generic

* somewhat working

* update to go 1.18
  • Loading branch information
haitch committed May 31, 2022
1 parent 55c33d0 commit 22f8251
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 221 deletions.
12 changes: 2 additions & 10 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,15 @@ jobs:
runs-on: ubuntu-latest
steps:

- name: Set up Go 1.13
- name: Set up Go 1.18
uses: actions/setup-go@v1
with:
go-version: 1.13
go-version: 1.18
id: go

- name: Check out code into the Go module directory
uses: actions/checkout@v2

- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Build
run: go build -v .

Expand Down
10 changes: 4 additions & 6 deletions continue_with.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ package asynctask
import "context"

// ContinueFunc is a function that can be connected to previous task with ContinueWith
type ContinueFunc func(context.Context, interface{}) (interface{}, error)
type ContinueFunc[T any, S any] func(context.Context, *T) (*S, error)

// ContinueWith start the function when current task is done.
// result from previous task will be passed in, if no error.
func (tsk *TaskStatus) ContinueWith(ctx context.Context, next ContinueFunc) *TaskStatus {
return Start(ctx, func(fCtx context.Context) (interface{}, error) {
result, err := tsk.Wait(fCtx)
func ContinueWith[T any, S any](ctx context.Context, tsk *Task[T], next ContinueFunc[T, S]) *Task[S] {
return Start(ctx, func(fCtx context.Context) (*S, error) {
result, err := tsk.Result(fCtx)
if err != nil {
return nil, err
}
Expand Down
36 changes: 18 additions & 18 deletions continue_with_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
"github.com/stretchr/testify/assert"
)

func getAdvancedCountingTask(countFrom, step int, sleepInterval time.Duration) asynctask.AsyncFunc {
return func(ctx context.Context) (interface{}, error) {
func getAdvancedCountingTask(countFrom int, step int, sleepInterval time.Duration) asynctask.AsyncFunc[int] {
return func(ctx context.Context) (*int, error) {
t := ctx.Value(testContextKey).(*testing.T)

result := countFrom
Expand All @@ -21,56 +21,56 @@ func getAdvancedCountingTask(countFrom, step int, sleepInterval time.Duration) a
result++
case <-ctx.Done():
t.Log("work canceled")
return result, nil
return &result, nil
}
}
return result, nil
return &result, nil
}
}

func TestContinueWith(t *testing.T) {
t.Parallel()
ctx := newTestContext(t)
t1 := asynctask.Start(ctx, getAdvancedCountingTask(0, 10, 20*time.Millisecond))
t2 := t1.ContinueWith(ctx, func(fCtx context.Context, input interface{}) (interface{}, error) {
fromPrevTsk := input.(int)
t2 := asynctask.ContinueWith(ctx, t1, func(fCtx context.Context, input *int) (*int, error) {
fromPrevTsk := *input
return getAdvancedCountingTask(fromPrevTsk, 10, 20*time.Millisecond)(fCtx)
})
t3 := t1.ContinueWith(ctx, func(fCtx context.Context, input interface{}) (interface{}, error) {
fromPrevTsk := input.(int)
t3 := asynctask.ContinueWith(ctx, t1, func(fCtx context.Context, input *int) (*int, error) {
fromPrevTsk := *input
return getAdvancedCountingTask(fromPrevTsk, 12, 20*time.Millisecond)(fCtx)
})

result, err := t2.Wait(ctx)
result, err := t2.Result(ctx)
assert.NoError(t, err)
assert.Equal(t, asynctask.StateCompleted, t2.State(), "Task should complete with no error")
assert.Equal(t, result, 20)
assert.Equal(t, *result, 20)

result, err = t3.Wait(ctx)
result, err = t3.Result(ctx)
assert.NoError(t, err)
assert.Equal(t, asynctask.StateCompleted, t3.State(), "Task should complete with no error")
assert.Equal(t, result, 22)
assert.Equal(t, *result, 22)
}

func TestContinueWithFailureCase(t *testing.T) {
t.Parallel()
ctx := newTestContext(t)
t1 := asynctask.Start(ctx, getErrorTask("devide by 0", 10*time.Millisecond))
t2 := t1.ContinueWith(ctx, func(fCtx context.Context, input interface{}) (interface{}, error) {
fromPrevTsk := input.(int)
t2 := asynctask.ContinueWith(ctx, t1, func(fCtx context.Context, input *int) (*int, error) {
fromPrevTsk := *input
return getAdvancedCountingTask(fromPrevTsk, 10, 20*time.Millisecond)(fCtx)
})
t3 := t1.ContinueWith(ctx, func(fCtx context.Context, input interface{}) (interface{}, error) {
fromPrevTsk := input.(int)
t3 := asynctask.ContinueWith(ctx, t1, func(fCtx context.Context, input *int) (*int, error) {
fromPrevTsk := *input
return getAdvancedCountingTask(fromPrevTsk, 12, 20*time.Millisecond)(fCtx)
})

_, err := t2.Wait(ctx)
_, err := t2.Result(ctx)
assert.Error(t, err)
assert.Equal(t, asynctask.StateFailed, t2.State(), "Task2 should fail since preceeding task failed")
assert.Equal(t, "devide by 0", err.Error())

_, err = t3.Wait(ctx)
_, err = t3.Result(ctx)
assert.Error(t, err)
assert.Equal(t, asynctask.StateFailed, t3.State(), "Task3 should fail since preceeding task failed")
assert.Equal(t, "devide by 0", err.Error())
Expand Down
70 changes: 6 additions & 64 deletions error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,15 @@ import (
"github.com/stretchr/testify/assert"
)

type structError struct{}

func (pe structError) Error() string {
return "Error from struct type"
}

type pointerError struct{}

func (pe *pointerError) Error() string {
return "Error from pointer type"
}

func getPanicTask(sleepDuration time.Duration) asynctask.AsyncFunc {
return func(ctx context.Context) (interface{}, error) {
func getPanicTask(sleepDuration time.Duration) asynctask.AsyncFunc[string] {
return func(ctx context.Context) (*string, error) {
time.Sleep(sleepDuration)
panic("yo")
}
}

func getErrorTask(errorString string, sleepDuration time.Duration) asynctask.AsyncFunc {
return func(ctx context.Context) (interface{}, error) {
func getErrorTask(errorString string, sleepDuration time.Duration) asynctask.AsyncFunc[int] {
return func(ctx context.Context) (*int, error) {
time.Sleep(sleepDuration)
return nil, errors.New(errorString)
}
Expand All @@ -49,12 +37,12 @@ func TestTimeoutCase(t *testing.T) {
// I can continue wait with longer time
rawResult, err := tsk.WaitWithTimeout(ctx, 2*time.Second)
assert.NoError(t, err)
assert.Equal(t, 9, rawResult)
assert.Equal(t, 9, *rawResult)

// any following Wait should complete immediately
rawResult, err = tsk.WaitWithTimeout(ctx, 2*time.Nanosecond)
assert.NoError(t, err)
assert.Equal(t, 9, rawResult)
assert.Equal(t, 9, *rawResult)
}

func TestPanicCase(t *testing.T) {
Expand All @@ -79,49 +67,3 @@ func TestErrorCase(t *testing.T) {
assert.False(t, errors.Is(err, context.DeadlineExceeded), "not expecting DeadlineExceeded")
assert.Equal(t, "dummy error", err.Error())
}

func TestPointerErrorCase(t *testing.T) {
t.Parallel()
ctx, cancelFunc := newTestContextWithTimeout(t, 3*time.Second)
defer cancelFunc()

// nil point of a type that implement error
var pe *pointerError = nil
// pass this nil pointer to error interface
var err error = pe
// now you get a non-nil error
assert.False(t, err == nil, "reason this test is needed")

tsk := asynctask.Start(ctx, func(ctx context.Context) (interface{}, error) {
time.Sleep(100 * time.Millisecond)
var pe *pointerError = nil
return "Done", pe
})

result, err := tsk.Wait(ctx)
assert.NoError(t, err)
assert.Equal(t, result, "Done")
}

func TestStructErrorCase(t *testing.T) {
t.Parallel()
ctx, cancelFunc := newTestContextWithTimeout(t, 3*time.Second)
defer cancelFunc()

// nil point of a type that implement error
var se structError
// pass this nil pointer to error interface
var err error = se
// now you get a non-nil error
assert.False(t, err == nil, "reason this test is needed")

tsk := asynctask.Start(ctx, func(ctx context.Context) (interface{}, error) {
time.Sleep(100 * time.Millisecond)
var se structError
return "Done", se
})

result, err := tsk.Wait(ctx)
assert.NoError(t, err)
assert.Equal(t, result, "Done")
}
10 changes: 8 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
module github.com/Azure/go-asynctask

go 1.13
go 1.18

require github.com/stretchr/testify v1.7.0
require github.com/stretchr/testify v1.7.1

require (
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
5 changes: 2 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
Expand Down
Loading

0 comments on commit 22f8251

Please sign in to comment.